morpheus-cli 5.5.0 → 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 (39) 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/scale_thresholds_interface.rb +9 -0
  7. data/lib/morpheus/cli/cli_command.rb +39 -20
  8. data/lib/morpheus/cli/commands/apps.rb +1 -1
  9. data/lib/morpheus/cli/commands/cloud_resource_pools_command.rb +33 -2
  10. data/lib/morpheus/cli/commands/clouds.rb +12 -6
  11. data/lib/morpheus/cli/commands/clusters.rb +66 -5
  12. data/lib/morpheus/cli/commands/hosts.rb +5 -1
  13. data/lib/morpheus/cli/commands/instances.rb +1 -1
  14. data/lib/morpheus/cli/commands/integrations_command.rb +1 -1
  15. data/lib/morpheus/cli/commands/invoices_command.rb +8 -1
  16. data/lib/morpheus/cli/commands/jobs_command.rb +45 -225
  17. data/lib/morpheus/cli/commands/library_container_types_command.rb +52 -3
  18. data/lib/morpheus/cli/commands/library_option_types_command.rb +56 -62
  19. data/lib/morpheus/cli/commands/load_balancers.rb +11 -19
  20. data/lib/morpheus/cli/commands/network_pool_servers_command.rb +5 -2
  21. data/lib/morpheus/cli/commands/roles.rb +475 -70
  22. data/lib/morpheus/cli/commands/scale_thresholds.rb +103 -0
  23. data/lib/morpheus/cli/commands/tasks.rb +19 -12
  24. data/lib/morpheus/cli/commands/user_sources_command.rb +107 -39
  25. data/lib/morpheus/cli/commands/users.rb +10 -10
  26. data/lib/morpheus/cli/commands/view.rb +1 -0
  27. data/lib/morpheus/cli/commands/workflows.rb +21 -14
  28. data/lib/morpheus/cli/error_handler.rb +13 -4
  29. data/lib/morpheus/cli/mixins/accounts_helper.rb +1 -1
  30. data/lib/morpheus/cli/mixins/execution_request_helper.rb +1 -1
  31. data/lib/morpheus/cli/mixins/infrastructure_helper.rb +3 -3
  32. data/lib/morpheus/cli/mixins/jobs_helper.rb +173 -0
  33. data/lib/morpheus/cli/mixins/print_helper.rb +120 -38
  34. data/lib/morpheus/cli/mixins/provisioning_helper.rb +1 -3
  35. data/lib/morpheus/cli/mixins/rest_command.rb +41 -14
  36. data/lib/morpheus/cli/option_types.rb +68 -37
  37. data/lib/morpheus/cli/version.rb +1 -1
  38. data/lib/morpheus/logging.rb +6 -8
  39. metadata +6 -4
@@ -166,6 +166,7 @@ EOT
166
166
  # role = json_response['role']
167
167
  load_whoami()
168
168
  json_response = nil
169
+ role = nil
169
170
  if args[0].to_s =~ /\A\d{1,}\Z/
170
171
  json_response = @roles_interface.get(account_id, args[0].to_i)
171
172
  role = json_response['role']
@@ -290,10 +291,12 @@ EOT
290
291
  print cyan
291
292
  # puts "Instance Type Access: #{get_access_string(json_response['globalInstanceTypeAccess'])}"
292
293
  # print "\n"
293
- if json_response['globalInstanceTypeAccess'] == 'custom'
294
+ instance_type_global_access = json_response['globalInstanceTypeAccess']
295
+ instance_type_permissions = role['instanceTypes'] ? role['instanceTypes'] : (json_response['instanceTypePermissions'] || [])
296
+ if instance_type_global_access == 'custom'
294
297
  print_h2 "Instance Type Access", options
295
298
  if options[:include_instance_type_access]
296
- rows = json_response['instanceTypePermissions'].collect do |it|
299
+ rows = instance_type_permissions.collect do |it|
297
300
  {
298
301
  name: it['name'],
299
302
  access: format_access_string(it['access'], ["none","read","full"]),
@@ -310,7 +313,7 @@ EOT
310
313
  end
311
314
 
312
315
  blueprint_global_access = json_response['globalAppTemplateAccess'] || json_response['globalBlueprintAccess']
313
- blueprint_permissions = json_response['appTemplatePermissions'] || json_response['blueprintPermissions'] || []
316
+ blueprint_permissions = (role['appTemplates'] || role['blueprints']) ? (role['appTemplates'] || role['blueprints']) : (json_response['appTemplatePermissions'] || json_response['blueprintPermissions'] || [])
314
317
  print cyan
315
318
  # print_h2 "Blueprint Access: #{get_access_string(json_response['globalAppTemplateAccess'])}", options
316
319
  # print "\n"
@@ -332,10 +335,9 @@ EOT
332
335
  # print "\n"
333
336
  # print cyan,bold,"Blueprint Access: #{get_access_string(json_response['globalAppTemplateAccess'])}",reset,"\n"
334
337
  end
335
-
336
338
 
337
339
  catalog_item_type_global_access = json_response['globalCatalogItemTypeAccess']
338
- catalog_item_type_permissions = json_response['catalogItemTypePermissions'] || []
340
+ catalog_item_type_permissions = role['catalogItemTypes'] ? role['catalogItemTypes'] : (json_response['catalogItemTypePermissions'] || [])
339
341
  print cyan
340
342
  # print_h2 "catalog_item_type Access: #{get_access_string(json_response['globalCatalogItemTypeAccess'])}", options
341
343
  # print "\n"
@@ -357,8 +359,7 @@ EOT
357
359
  # print cyan,bold,"Catalog Item Type Access: #{get_access_string(json_response['globalCatalogItemTypeAccess'])}",reset,"\n"
358
360
  end
359
361
 
360
-
361
- persona_permissions = json_response['personaPermissions'] || json_response['personas'] || []
362
+ persona_permissions = role['personas'] ? role['personas'] : (json_response['personaPermissions'] || [])
362
363
  # if options[:include_personas_access]
363
364
  print cyan
364
365
  if persona_permissions
@@ -375,7 +376,7 @@ EOT
375
376
  # print reset,"\n"
376
377
 
377
378
  vdi_pool_global_access = json_response['globalVdiPoolAccess']
378
- vdi_pool_permissions = json_response['vdiPoolPermissions'] || []
379
+ vdi_pool_permissions = role['vdiPools'] ? role['vdiPools'] : (json_response['vdiPoolPermissions'] || [])
379
380
  print cyan
380
381
  if vdi_pool_global_access == 'custom'
381
382
  print_h2 "VDI Pool Access", options
@@ -396,7 +397,7 @@ EOT
396
397
  end
397
398
 
398
399
  report_type_global_access = json_response['globalReportTypeAccess']
399
- report_type_permissions = json_response['reportTypePermissions'] || []
400
+ report_type_permissions = role['reportTypes'] ? role['reportTypes'] : (json_response['reportTypePermissions'] || [])
400
401
  print cyan
401
402
  if report_type_global_access == 'custom'
402
403
  print_h2 "Report Type Access", options
@@ -513,12 +514,83 @@ EOT
513
514
  optparse = Morpheus::Cli::OptionParser.new do |opts|
514
515
  opts.banner = subcommand_usage("[name] [options]")
515
516
  build_option_type_options(opts, options, add_role_option_types)
517
+ opts.on('--permissions CODE=ACCESS', String, "Set feature permission access by permission code. Example: dashboard=read,operations-wiki=full" ) do |val|
518
+ options[:permissions] ||= {}
519
+ parse_access_csv(options[:permissions], val, args, optparse)
520
+ end
521
+ opts.on('--global-group-access ACCESS', String, "Update the global group (site) access: [none|read|custom|full]" ) do |val|
522
+ params['globalSiteAccess'] = val.to_s.downcase
523
+ end
524
+ opts.on('--groups ID=ACCESS', String, "Set group (site) to a custom access by group id. Example: 1=none,2=full,3=read" ) do |val|
525
+ options[:group_permissions] ||= {}
526
+ parse_access_csv(options[:group_permissions], val, args, optparse)
527
+ end
528
+ opts.on('--global-cloud-access ACCESS', String, "Update the global cloud (zone) access: [none|custom|full]" ) do |val|
529
+ params['globalZoneAccess'] = val.to_s.downcase
530
+ end
531
+ opts.on('--clouds ID=ACCESS', String, "Set cloud (zone) to a custom access by cloud id. Example: 1=none,2=full,3=read" ) do |val|
532
+ options[:cloud_permissions] ||= {}
533
+ parse_access_csv(options[:cloud_permissions], val, args, optparse)
534
+ end
535
+ opts.on('--global-instance-type-access ACCESS', String, "Update the global instance type access: [none|custom|full]" ) do |val|
536
+ params['globalInstanceTypeAccess'] = val.to_s.downcase
537
+ end
538
+ opts.on('--instance-types CODE=ACCESS', String, "Set instance type to a custom access instance type code. Example: nginx=full,apache=none" ) do |val|
539
+ options[:instance_type_permissions] ||= {}
540
+ parse_access_csv(options[:instance_type_permissions], val, args, optparse)
541
+ end
542
+ opts.on('--global-blueprint-access ACCESS', String, "Update the global blueprint access: [none|custom|full]" ) do |val|
543
+ params['globalAppTemplateAccess'] = val.to_s.downcase
544
+ end
545
+ opts.on('--blueprints ID=ACCESS', String, "Set blueprint to a custom access by blueprint id. Example: 1=full,2=none" ) do |val|
546
+ options[:blueprint_permissions] ||= {}
547
+ parse_access_csv(options[:blueprint_permissions], val, args, optparse)
548
+ end
549
+ opts.on('--global-catalog-item-type-access ACCESS', String, "Update the global catalog item type access: [none|custom|full]" ) do |val|
550
+ params['globalCatalogItemTypeAccess'] = val.to_s.downcase
551
+ end
552
+ opts.on('--catalog-item-types CODE=ACCESS', String, "Set catalog item type to a custom access by catalog item type id. Example: 1=full,2=none" ) do |val|
553
+ options[:catalog_item_type_permissions] ||= {}
554
+ parse_access_csv(options[:catalog_item_type_permissions], val, args, optparse)
555
+ end
556
+ opts.on('--personas CODE=ACCESS', String, "Set persona to a custom access by persona code. Example: standard=full,serviceCatalog=full,vdi=full" ) do |val|
557
+ options[:persona_permissions] ||= {}
558
+ parse_access_csv(options[:persona_permissions], val, args, optparse)
559
+ end
560
+ opts.on('--global-vdi-pool-access-access ACCESS', String, "Update the global VDI pool access: [none|custom|full]" ) do |val|
561
+ params['globalVdiPoolAccess'] = val.to_s.downcase
562
+ end
563
+ opts.on('--vdi-pools ID=ACCESS', String, "Set VDI pool to a custom access by VDI pool id. Example: 1=full,2=none" ) do |val|
564
+ options[:vdi_pool_permissions] ||= {}
565
+ parse_access_csv(options[:vdi_pool_permissions], val, args, optparse)
566
+ end
567
+ opts.on('--global-report-type-access ACCESS', String, "Update the global report type access: [none|custom|full]" ) do |val|
568
+ params['globalReportTypeAccess'] = val.to_s.downcase
569
+ end
570
+ opts.on('--report-types CODE=ACCESS', String, "Set report type to a custom access by report type code. Example: appCost=none,guidance=full" ) do |val|
571
+ options[:report_type_permissions] ||= {}
572
+ parse_access_csv(options[:report_type_permissions], val, args, optparse)
573
+ end
574
+ opts.on('--reset-permissions', "Reset all feature permission access to none. This can be used in conjunction with --permissions to recreate the feature permission access for the role." ) do
575
+ options[:reset_permissions] = true
576
+ end
577
+ opts.on('--reset-all-access', "Reset all access to none including permissions, global groups, instance types, etc. This can be used in conjunction with --permissions to recreate the feature permission access for the role." ) do
578
+ options[:reset_all_access] = true
579
+ end
580
+ opts.footer = <<-EOT
581
+ Create a new role.
582
+ [name] is required. This is a unique name (authority) for the new role.
583
+ All the role permissions and access values can be configured.
584
+ Use --permissions "CODE=ACCESS,CODE=ACCESS" to update access levels for specific feature permissions identified by code.
585
+ Use --global-instance-type-access custom --instance-types "CODE=ACCESS,CODE=ACCESS" to customize instance type access.
586
+ Only the specified permissions,instance types, etc. are updated.
587
+ Use --reset-permissions to set access to "none" for all unspecified feature permissions.
588
+ Use --reset-all-access to set access to "none" for all unspecified feature permissions and global access values for groups, instance types, etc.
589
+ EOT
516
590
  build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote])
517
591
  end
518
592
  optparse.parse!(args)
519
- if args.count > 1
520
- raise_command_error "wrong number of arguments, expected 0-1 and got (#{args.count}) #{args}\n#{optparse}"
521
- end
593
+ verify_args!(args:args, optparse:optparse, max:1)
522
594
  if args[0]
523
595
  options[:options]['authority'] = args[0]
524
596
  end
@@ -566,7 +638,7 @@ EOT
566
638
  v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'multitenant', 'fieldLabel' => 'Multitenant', 'type' => 'checkbox', 'defaultValue' => 'off', 'description' => 'A Multitenant role is automatically copied into all existing subaccounts as well as placed into a subaccount when created. Useful for providing a set of predefined roles a Customer can use', 'displayOrder' => 5}], options[:options])
567
639
  role_payload['multitenant'] = ['on','true'].include?(v_prompt['multitenant'].to_s)
568
640
  if role_payload['multitenant']
569
- v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'multitenantLocked', 'fieldLabel' => 'Multitenant Locked', 'type' => 'checkbox', 'defaultValue' => 'off', 'description' => 'Prevents subtenants from branching off this role/modifying it. '}], options[:options])
641
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'multitenantLocked', 'fieldLabel' => 'Multitenant Locked', 'type' => 'checkbox', 'defaultValue' => 'off', 'description' => 'Prevents subtenants from branching off this role/modifying it.'}], options[:options])
570
642
  role_payload['multitenantLocked'] = ['on','true'].include?(v_prompt['multitenantLocked'].to_s)
571
643
  end
572
644
  end
@@ -576,6 +648,116 @@ EOT
576
648
  v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'defaultPersona', 'fieldLabel' => 'Default Persona', 'type' => 'select', 'selectOptions' => get_persona_select_options(), 'description' => 'Default Persona'}], options[:options], @api_client)
577
649
  role_payload['defaultPersona'] = {'code' => v_prompt['defaultPersona']} unless v_prompt['defaultPersona'].to_s.strip.empty?
578
650
 
651
+ # bulk permissions
652
+ if options[:permissions]
653
+ perms_array = []
654
+ options[:permissions].each do |k,v|
655
+ perm_code = k
656
+ access_value = v.to_s.empty? ? "none" : v.to_s
657
+ perms_array << {"code" => perm_code, "access" => access_value}
658
+ end
659
+ params['permissions'] = perms_array
660
+ end
661
+ if options[:group_permissions]
662
+ perms_array = []
663
+ options[:group_permissions].each do |k,v|
664
+ site_id = k
665
+ access_value = v.to_s.empty? ? "none" : v.to_s
666
+ if site_id =~ /\A\d{1,}\Z/
667
+ perms_array << {"id" => site_id.to_i, "access" => access_value}
668
+ else
669
+ perms_array << {"name" => site_id, "access" => access_value}
670
+ end
671
+ end
672
+ params['sites'] = perms_array
673
+ end
674
+ if options[:cloud_permissions]
675
+ perms_array = []
676
+ options[:cloud_permissions].each do |k,v|
677
+ zone_id = k
678
+ access_value = v.to_s.empty? ? "none" : v.to_s
679
+ if zone_id =~ /\A\d{1,}\Z/
680
+ perms_array << {"id" => zone_id.to_i, "access" => access_value}
681
+ else
682
+ perms_array << {"name" => zone_id, "access" => access_value}
683
+ end
684
+ perms_array << {"id" => zone_id, "access" => access_value}
685
+ end
686
+ params['zones'] = perms_array
687
+ end
688
+ if options[:instance_type_permissions]
689
+ perms_array = []
690
+ options[:instance_type_permissions].each do |k,v|
691
+ instance_type_code = k
692
+ access_value = v.to_s.empty? ? "none" : v.to_s
693
+ perms_array << {"code" => instance_type_code, "access" => access_value}
694
+ end
695
+ params['instanceTypes'] = perms_array
696
+ end
697
+ if options[:blueprint_permissions]
698
+ perms_array = []
699
+ options[:blueprint_permissions].each do |k,v|
700
+ blueprint_id = k
701
+ access_value = v.to_s.empty? ? "none" : v.to_s
702
+ if blueprint_id =~ /\A\d{1,}\Z/
703
+ perms_array << {"id" => blueprint_id.to_i, "access" => access_value}
704
+ else
705
+ perms_array << {"name" => blueprint_id, "access" => access_value}
706
+ end
707
+ end
708
+ params['appTemplates'] = perms_array
709
+ end
710
+ if options[:catalog_item_type_permissions]
711
+ perms_array = []
712
+ options[:catalog_item_type_permissions].each do |k,v|
713
+ catalog_item_type_id = k
714
+ access_value = v.to_s.empty? ? "none" : v.to_s
715
+ if catalog_item_type_id =~ /\A\d{1,}\Z/
716
+ perms_array << {"id" => catalog_item_type_id.to_i, "access" => access_value}
717
+ else
718
+ perms_array << {"name" => catalog_item_type_id, "access" => access_value}
719
+ end
720
+ end
721
+ params['catalogItemTypes'] = perms_array
722
+
723
+ end
724
+ if options[:persona_permissions]
725
+ perms_array = []
726
+ options[:persona_permissions].each do |k,v|
727
+ persona_code = k
728
+ access_value = v.to_s.empty? ? "none" : v.to_s
729
+ perms_array << {"code" => persona_code, "access" => access_value}
730
+ end
731
+ params['personas'] = perms_array
732
+ end
733
+ if options[:vdi_pool_permissions]
734
+ perms_array = []
735
+ options[:vdi_pool_permissions].each do |k,v|
736
+ vdi_pool_id = k
737
+ access_value = v.to_s.empty? ? "none" : v.to_s
738
+ if vdi_pool_id =~ /\A\d{1,}\Z/
739
+ perms_array << {"id" => vdi_pool_id.to_i, "access" => access_value}
740
+ else
741
+ perms_array << {"name" => vdi_pool_id, "access" => access_value}
742
+ end
743
+ end
744
+ params['vdiPools'] = perms_array
745
+ end
746
+ if options[:report_type_permissions]
747
+ perms_array = []
748
+ options[:report_type_permissions].each do |k,v|
749
+ report_type_code = k
750
+ access_value = v.to_s.empty? ? "none" : v.to_s
751
+ perms_array << {"code" => report_type_code, "access" => access_value}
752
+ end
753
+ params['reportTypes'] = perms_array
754
+ end
755
+ if options[:reset_permissions]
756
+ params["resetPermissions"] = true
757
+ end
758
+ if options[:reset_all_access]
759
+ params["resetAllAccess"] = true
760
+ end
579
761
  payload = {"role" => role_payload}
580
762
  end
581
763
  @roles_interface.setopts(options)
@@ -622,14 +804,85 @@ EOT
622
804
  optparse = Morpheus::Cli::OptionParser.new do |opts|
623
805
  opts.banner = subcommand_usage("[role] [options]")
624
806
  build_option_type_options(opts, options, update_role_option_types)
625
- build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote])
807
+ opts.on('--permissions CODE=ACCESS', String, "Set feature permission access by permission code. Example: dashboard=read,operations-wiki=full" ) do |val|
808
+ options[:permissions] ||= {}
809
+ parse_access_csv(options[:permissions], val, args, optparse)
810
+ end
811
+ opts.on('--global-group-access ACCESS', String, "Update the global group (site) access: [none|read|custom|full]" ) do |val|
812
+ params['globalSiteAccess'] = val.to_s.downcase
813
+ end
814
+ opts.on('--groups ID=ACCESS', String, "Set group (site) to a custom access by group id. Example: 1=none,2=full,3=read" ) do |val|
815
+ options[:group_permissions] ||= {}
816
+ parse_access_csv(options[:group_permissions], val, args, optparse)
817
+ end
818
+ opts.on('--global-cloud-access ACCESS', String, "Update the global cloud (zone) access: [none|custom|full]" ) do |val|
819
+ params['globalZoneAccess'] = val.to_s.downcase
820
+ end
821
+ opts.on('--clouds ID=ACCESS', String, "Set cloud (zone) to a custom access by cloud id. Example: 1=none,2=full,3=read" ) do |val|
822
+ options[:cloud_permissions] ||= {}
823
+ parse_access_csv(options[:cloud_permissions], val, args, optparse)
824
+ end
825
+ opts.on('--global-instance-type-access ACCESS', String, "Update the global instance type access: [none|custom|full]" ) do |val|
826
+ params['globalInstanceTypeAccess'] = val.to_s.downcase
827
+ end
828
+ opts.on('--instance-types CODE=ACCESS', String, "Set instance type to a custom access instance type code. Example: nginx=full,apache=none" ) do |val|
829
+ options[:instance_type_permissions] ||= {}
830
+ parse_access_csv(options[:instance_type_permissions], val, args, optparse)
831
+ end
832
+ opts.on('--global-blueprint-access ACCESS', String, "Update the global blueprint access: [none|custom|full]" ) do |val|
833
+ params['globalAppTemplateAccess'] = val.to_s.downcase
834
+ end
835
+ opts.on('--blueprints ID=ACCESS', String, "Set blueprint to a custom access by blueprint id. Example: 1=full,2=none" ) do |val|
836
+ options[:blueprint_permissions] ||= {}
837
+ parse_access_csv(options[:blueprint_permissions], val, args, optparse)
838
+ end
839
+ opts.on('--global-catalog-item-type-access ACCESS', String, "Update the global catalog item type access: [none|custom|full]" ) do |val|
840
+ params['globalCatalogItemTypeAccess'] = val.to_s.downcase
841
+ end
842
+ opts.on('--catalog-item-types CODE=ACCESS', String, "Set catalog item type to a custom access by catalog item type id. Example: 1=full,2=none" ) do |val|
843
+ options[:catalog_item_type_permissions] ||= {}
844
+ parse_access_csv(options[:catalog_item_type_permissions], val, args, optparse)
845
+ end
846
+ opts.on('--personas CODE=ACCESS', String, "Set persona to a custom access by persona code. Example: standard=full,serviceCatalog=full,vdi=full" ) do |val|
847
+ options[:persona_permissions] ||= {}
848
+ parse_access_csv(options[:persona_permissions], val, args, optparse)
849
+ end
850
+ opts.on('--global-vdi-pool-access-access ACCESS', String, "Update the global VDI pool access: [none|custom|full]" ) do |val|
851
+ params['globalVdiPoolAccess'] = val.to_s.downcase
852
+ end
853
+ opts.on('--vdi-pools ID=ACCESS', String, "Set VDI pool to a custom access by VDI pool id. Example: 1=full,2=none" ) do |val|
854
+ options[:vdi_pool_permissions] ||= {}
855
+ parse_access_csv(options[:vdi_pool_permissions], val, args, optparse)
856
+ end
857
+ opts.on('--global-report-type-access ACCESS', String, "Update the global report type access: [none|custom|full]" ) do |val|
858
+ params['globalReportTypeAccess'] = val.to_s.downcase
859
+ end
860
+ opts.on('--report-types CODE=ACCESS', String, "Set report type to a custom access by report type code. Example: appCost=none,guidance=full" ) do |val|
861
+ options[:report_type_permissions] ||= {}
862
+ parse_access_csv(options[:report_type_permissions], val, args, optparse)
863
+ end
864
+ opts.on('--reset-permissions', "Reset all feature permission access to none. This can be used in conjunction with --permissions to recreate the feature permission access for the role." ) do
865
+ options[:reset_permissions] = true
866
+ end
867
+ opts.on('--reset-all-access', "Reset all access to none including permissions, global groups, instance types, etc. This can be used in conjunction with --permissions to recreate the feature permission access for the role." ) do
868
+ options[:reset_all_access] = true
869
+ end
870
+ build_standard_update_options(opts, options)
871
+ opts.footer = <<-EOT
872
+ Update a role.
873
+ [role] is required. This is the name (authority) or id of a role.
874
+ All the role permissions and access values can be configured.
875
+ Use --permissions "CODE=ACCESS,CODE=ACCESS" to update access levels for specific feature permissions identified by code.
876
+ Use --global-instance-type-access custom --instance-types "CODE=ACCESS,CODE=ACCESS" to customize instance type access.
877
+ Only the specified permissions,instance types, etc. are updated.
878
+ Use --reset-permissions to set access to "none" for all unspecified feature permissions.
879
+ Use --reset-all-access to set access to "none" for all unspecified feature permissions and global access values for groups, instance types, etc.
880
+ EOT
626
881
  end
627
882
  optparse.parse!(args)
628
883
 
629
- if args.count < 1
630
- puts optparse
631
- exit 1
632
- end
884
+ verify_args!(args:args, optparse:optparse, count:1)
885
+
633
886
  name = args[0]
634
887
  connect(options)
635
888
  begin
@@ -659,13 +912,119 @@ EOT
659
912
  end
660
913
  #params = Morpheus::Cli::OptionTypes.prompt(prompt_option_types, options[:options], @api_client, options[:params])
661
914
 
662
- if params.empty?
663
- puts optparse
664
- option_lines = prompt_option_types.collect {|it| "\t-O #{it['fieldName']}=\"value\"" }.join("\n")
665
- puts "\nAvailable Options:\n#{option_lines}\n\n"
666
- exit 1
915
+ # bulk permissions
916
+ if options[:permissions]
917
+ perms_array = []
918
+ options[:permissions].each do |k,v|
919
+ perm_code = k
920
+ access_value = v.to_s.empty? ? "none" : v.to_s
921
+ perms_array << {"code" => perm_code, "access" => access_value}
922
+ end
923
+ params['permissions'] = perms_array
924
+ end
925
+ if options[:group_permissions]
926
+ perms_array = []
927
+ options[:group_permissions].each do |k,v|
928
+ site_id = k
929
+ access_value = v.to_s.empty? ? "none" : v.to_s
930
+ if site_id =~ /\A\d{1,}\Z/
931
+ perms_array << {"id" => site_id.to_i, "access" => access_value}
932
+ else
933
+ perms_array << {"name" => site_id, "access" => access_value}
934
+ end
935
+ end
936
+ params['sites'] = perms_array
937
+ end
938
+ if options[:cloud_permissions]
939
+ perms_array = []
940
+ options[:cloud_permissions].each do |k,v|
941
+ zone_id = k
942
+ access_value = v.to_s.empty? ? "none" : v.to_s
943
+ if zone_id =~ /\A\d{1,}\Z/
944
+ perms_array << {"id" => zone_id.to_i, "access" => access_value}
945
+ else
946
+ perms_array << {"name" => zone_id, "access" => access_value}
947
+ end
948
+ perms_array << {"id" => zone_id, "access" => access_value}
949
+ end
950
+ params['zones'] = perms_array
667
951
  end
952
+ if options[:instance_type_permissions]
953
+ perms_array = []
954
+ options[:instance_type_permissions].each do |k,v|
955
+ instance_type_code = k
956
+ access_value = v.to_s.empty? ? "none" : v.to_s
957
+ perms_array << {"code" => instance_type_code, "access" => access_value}
958
+ end
959
+ params['instanceTypes'] = perms_array
960
+ end
961
+ if options[:blueprint_permissions]
962
+ perms_array = []
963
+ options[:blueprint_permissions].each do |k,v|
964
+ blueprint_id = k
965
+ access_value = v.to_s.empty? ? "none" : v.to_s
966
+ if blueprint_id =~ /\A\d{1,}\Z/
967
+ perms_array << {"id" => blueprint_id.to_i, "access" => access_value}
968
+ else
969
+ perms_array << {"name" => blueprint_id, "access" => access_value}
970
+ end
971
+ end
972
+ params['appTemplates'] = perms_array
973
+ end
974
+ if options[:catalog_item_type_permissions]
975
+ perms_array = []
976
+ options[:catalog_item_type_permissions].each do |k,v|
977
+ catalog_item_type_id = k
978
+ access_value = v.to_s.empty? ? "none" : v.to_s
979
+ if catalog_item_type_id =~ /\A\d{1,}\Z/
980
+ perms_array << {"id" => catalog_item_type_id.to_i, "access" => access_value}
981
+ else
982
+ perms_array << {"name" => catalog_item_type_id, "access" => access_value}
983
+ end
984
+ end
985
+ params['catalogItemTypes'] = perms_array
668
986
 
987
+ end
988
+ if options[:persona_permissions]
989
+ perms_array = []
990
+ options[:persona_permissions].each do |k,v|
991
+ persona_code = k
992
+ access_value = v.to_s.empty? ? "none" : v.to_s
993
+ perms_array << {"code" => persona_code, "access" => access_value}
994
+ end
995
+ params['personas'] = perms_array
996
+ end
997
+ if options[:vdi_pool_permissions]
998
+ perms_array = []
999
+ options[:vdi_pool_permissions].each do |k,v|
1000
+ vdi_pool_id = k
1001
+ access_value = v.to_s.empty? ? "none" : v.to_s
1002
+ if vdi_pool_id =~ /\A\d{1,}\Z/
1003
+ perms_array << {"id" => vdi_pool_id.to_i, "access" => access_value}
1004
+ else
1005
+ perms_array << {"name" => vdi_pool_id, "access" => access_value}
1006
+ end
1007
+ end
1008
+ params['vdiPools'] = perms_array
1009
+ end
1010
+ if options[:report_type_permissions]
1011
+ perms_array = []
1012
+ options[:report_type_permissions].each do |k,v|
1013
+ report_type_code = k
1014
+ access_value = v.to_s.empty? ? "none" : v.to_s
1015
+ perms_array << {"code" => report_type_code, "access" => access_value}
1016
+ end
1017
+ params['reportTypes'] = perms_array
1018
+ end
1019
+ if options[:reset_permissions]
1020
+ params["resetPermissions"] = true
1021
+ end
1022
+ if options[:reset_all_access]
1023
+ params["resetAllAccess"] = true
1024
+ end
1025
+ if params.empty?
1026
+ raise_command_error "Specify at least one option to update.\n#{optparse}"
1027
+ end
669
1028
  payload = {"role" => params}
670
1029
  end
671
1030
  @roles_interface.setopts(options)
@@ -674,20 +1033,18 @@ EOT
674
1033
  return
675
1034
  end
676
1035
  json_response = @roles_interface.update(account_id, role['id'], payload)
677
- if options[:json]
678
- print JSON.pretty_generate(json_response)
679
- print "\n"
680
- return
681
- end
682
- role = json_response['role']
683
- display_name = role['authority'] rescue ''
684
- print_green_success "Updated role #{display_name}"
1036
+ render_response(json_response, options, "role") do
1037
+ role = json_response['role']
1038
+ display_name = role['authority'] rescue ''
1039
+ print_green_success "Updated role #{display_name}"
685
1040
 
686
- get_args = [role['id']] + (options[:remote] ? ["-r",options[:remote]] : [])
687
- if account
688
- get_args.push "--account-id", account['id'].to_s
1041
+ get_args = [role['id']] + (options[:remote] ? ["-r",options[:remote]] : [])
1042
+ if account
1043
+ get_args.push "--account-id", account['id'].to_s
1044
+ end
1045
+ get(get_args)
689
1046
  end
690
- get(get_args)
1047
+ return 0, nil
691
1048
 
692
1049
  rescue RestClient::Exception => e
693
1050
  print_rest_exception(e, options)
@@ -700,6 +1057,10 @@ EOT
700
1057
  optparse = Morpheus::Cli::OptionParser.new do |opts|
701
1058
  opts.banner = subcommand_usage("[role]")
702
1059
  build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :remote])
1060
+ opts.footer = <<-EOT
1061
+ Delete a role.
1062
+ [role] is required. This is the name (authority) or id of a role.
1063
+ EOT
703
1064
  end
704
1065
  optparse.parse!(args)
705
1066
  if args.count < 1
@@ -737,22 +1098,43 @@ EOT
737
1098
  end
738
1099
 
739
1100
  def update_feature_access(args)
740
- usage = "Usage: morpheus roles update-feature-access [name] [code] [full|read|user|yes|no|none]"
741
1101
  options = {}
1102
+ allowed_access_values = ['full', 'user', 'read', 'none'] # just for display , veries per permission
1103
+ permission_code = nil
1104
+ access_value = nil
742
1105
  optparse = Morpheus::Cli::OptionParser.new do |opts|
743
- opts.banner = subcommand_usage("[name] [code] [full|read|user|yes|no|none]")
1106
+ opts.banner = subcommand_usage("[role] [permission] [access]")
1107
+ opts.on( '-p', '--permission CODE', "Permission code or name" ) do |val|
1108
+ permission_code = val
1109
+ end
1110
+ opts.on( '--access VALUE', String, "Access value [#{allowed_access_values.join('|')}] (varies per permission)" ) do |val|
1111
+ access_value = val
1112
+ end
744
1113
  build_common_options(opts, options, [:json, :dry_run, :remote])
1114
+ opts.footer = <<-EOT
1115
+ Update role access for a permission.
1116
+ [role] is required. This is the name (authority) or id of a role.
1117
+ [permission] is required. This is the code or name of a permission.
1118
+ [access] is required. This is the new access value: #{ored_list(allowed_access_values)}
1119
+ EOT
745
1120
  end
746
1121
  optparse.parse!(args)
747
-
748
- if args.count < 3
749
- puts optparse
750
- exit 1
751
- end
1122
+ verify_args!(args:args, optparse:optparse, min:1, max:3)
752
1123
  name = args[0]
753
- permission_code = args[1]
754
- access_value = args[2].to_s.downcase
1124
+ permission_code = args[1] if args[1]
1125
+ access_value = args[2].to_s.downcase if args[2]
755
1126
 
1127
+ if !permission_code
1128
+ raise_command_error("missing required argument: [permission]", args, optparse)
1129
+ end
1130
+ if !access_value
1131
+ raise_command_error("missing required argument: [access]", args, optparse)
1132
+ end
1133
+ # access_value = access_value.to_s.downcase
1134
+ # if !allowed_access_values.include?(access_value)
1135
+ # raise_command_error("invalid access value: #{access_value}", args, optparse)
1136
+ # end
1137
+ # need to load the permission and then split accessTypes, so just allows all for now, server validates...
756
1138
  # if !['full_decrypted','full', 'read', 'custom', 'none'].include?(access_value)
757
1139
  # puts optparse
758
1140
  # exit 1
@@ -786,15 +1168,16 @@ EOT
786
1168
  end
787
1169
 
788
1170
  def update_global_group_access(args)
789
- usage = "Usage: morpheus roles update-global-group-access [name] [full|read|custom|none]"
1171
+ usage = "Usage: morpheus roles update-global-group-access [role] [full|read|custom|none]"
790
1172
  options = {}
791
1173
  optparse = Morpheus::Cli::OptionParser.new do |opts|
792
- opts.banner = subcommand_usage("[name] [full|read|custom|none]")
1174
+ opts.banner = subcommand_usage("[role] [full|read|custom|none]")
793
1175
  build_common_options(opts, options, [:json, :dry_run, :remote])
794
- opts.footer = <<-EOT
1176
+ opts.footer = <<-EOT
795
1177
  Update global group access for a role.
796
1178
  [role] is required. This is the name (authority) or id of a role.
797
1179
  [access] is required. This is the access level to assign: full, read, custom or none.
1180
+ Only applicable to User roles.
798
1181
  EOT
799
1182
  end
800
1183
  optparse.parse!(args)
@@ -856,10 +1239,14 @@ EOT
856
1239
  access_value = val
857
1240
  end
858
1241
  build_common_options(opts, options, [:json, :dry_run, :remote])
859
- opts.footer = "Update role access for a group or all groups.\n" +
860
- "[role] is required. This is the name or id of a role.\n" +
861
- "--group or --all is required. This is the name or id of a group.\n" +
862
- "--access is required. This is the new access value. #{anded_list(allowed_access_values)}"
1242
+ opts.footer = <<-EOT
1243
+ Update role access for a group or all groups.
1244
+ [role] is required. This is the name or id of a role.
1245
+ --group or --all is required. This is the name or id of a group.
1246
+ --access is required. This is the new access value: #{ored_list(allowed_access_values)}
1247
+ Only applicable to User roles and when global group access is set to "custom".
1248
+ EOT
1249
+
863
1250
  end
864
1251
  optparse.parse!(args)
865
1252
 
@@ -939,15 +1326,16 @@ EOT
939
1326
  end
940
1327
 
941
1328
  def update_global_cloud_access(args)
942
- usage = "Usage: morpheus roles update-global-cloud-access [name] [full|custom|none]"
1329
+ usage = "Usage: morpheus roles update-global-cloud-access [role] [full|custom|none]"
943
1330
  options = {}
944
1331
  optparse = Morpheus::Cli::OptionParser.new do |opts|
945
- opts.banner = subcommand_usage("[name] [full|custom|none]")
1332
+ opts.banner = subcommand_usage("[role] [full|custom|none]")
946
1333
  build_common_options(opts, options, [:json, :dry_run, :remote])
947
1334
  opts.footer = <<-EOT
948
1335
  Update global cloud access for a role.
949
1336
  [role] is required. This is the name (authority) or id of a role.
950
1337
  [access] is required. This is the access level to assign: full, custom or none.
1338
+ Only applicable to Tenant roles.
951
1339
  EOT
952
1340
  end
953
1341
  optparse.parse!(args)
@@ -997,7 +1385,7 @@ EOT
997
1385
  do_all = false
998
1386
  allowed_access_values = ['full', 'read', 'none']
999
1387
  optparse = Morpheus::Cli::OptionParser.new do |opts|
1000
- opts.banner = subcommand_usage("[name]")
1388
+ opts.banner = subcommand_usage("[role]")
1001
1389
  opts.on( '-c', '--cloud CLOUD', "Cloud name or id" ) do |val|
1002
1390
  cloud_id = val
1003
1391
  end
@@ -1008,10 +1396,13 @@ EOT
1008
1396
  access_value = val
1009
1397
  end
1010
1398
  build_common_options(opts, options, [:json, :dry_run, :remote])
1011
- opts.footer = "Update role access for a cloud or all clouds.\n" +
1012
- "[role] is required. This is the name or id of a role.\n" +
1013
- "--cloud or --all is required. This is the name or id of a cloud.\n" +
1014
- "--access is required. This is the new access value. #{anded_list(allowed_access_values)}"
1399
+ opts.footer = <<-EOT
1400
+ Update role access for a cloud or all clouds.
1401
+ [role] is required. This is the name or id of a role.
1402
+ --cloud or --all is required. This is the name or id of a cloud.
1403
+ --access is required. This is the new access value: #{ored_list(allowed_access_values)}
1404
+ Only applicable to Tenant roles and when global cloud access is set to "custom".
1405
+ EOT
1015
1406
  end
1016
1407
  optparse.parse!(args)
1017
1408
 
@@ -1163,7 +1554,7 @@ EOT
1163
1554
  opts.footer = "Update role access for an instance type or all instance types.\n" +
1164
1555
  "[role] is required. This is the name or id of a role.\n" +
1165
1556
  "--instance-type or --all is required. This is the name of an instance type.\n" +
1166
- "--access is required. This is the new access value. #{anded_list(allowed_access_values)}"
1557
+ "--access is required. This is the new access value: #{ored_list(allowed_access_values)}"
1167
1558
  end
1168
1559
  optparse.parse!(args)
1169
1560
 
@@ -1311,10 +1702,10 @@ EOT
1311
1702
  access_value = val
1312
1703
  end
1313
1704
  build_common_options(opts, options, [:json, :dry_run, :remote])
1314
- opts.footer = "Update role access for an blueprint or all blueprints.\n" +
1705
+ opts.footer = "Update role access for a blueprint or all blueprints.\n" +
1315
1706
  "[role] is required. This is the name or id of a role.\n" +
1316
1707
  "--blueprint or --all is required. This is the name or id of a blueprint.\n" +
1317
- "--access is required. This is the new access value. #{anded_list(allowed_access_values)}"
1708
+ "--access is required. This is the new access value: #{ored_list(allowed_access_values)}"
1318
1709
  end
1319
1710
  optparse.parse!(args)
1320
1711
 
@@ -1475,10 +1866,10 @@ EOT
1475
1866
  access_value = val
1476
1867
  end
1477
1868
  build_common_options(opts, options, [:json, :dry_run, :remote])
1478
- opts.footer = "Update role access for an catalog item type or all types.\n" +
1869
+ opts.footer = "Update role access for a catalog item type or all types.\n" +
1479
1870
  "[role] is required. This is the name or id of a role.\n" +
1480
1871
  "--catalog-item-type or --all is required. This is the name or id of a catalog item type.\n" +
1481
- "--access is required. This is the new access value. #{anded_list(allowed_access_values)}"
1872
+ "--access is required. This is the new access value: #{ored_list(allowed_access_values)}"
1482
1873
  end
1483
1874
  optparse.parse!(args)
1484
1875
 
@@ -1513,7 +1904,7 @@ EOT
1513
1904
 
1514
1905
  role_json = @roles_interface.get(account_id, role['id'])
1515
1906
  catalog_item_type_global_access = role_json['globalCatalogItemTypeAccess']
1516
- catalog_item_type_permissions = role_json['catalogItemTypePermissions'] || []
1907
+ catalog_item_type_permissions = role_json['catalogItemTypePermissions'] || role_json['catalogItemTypes'] []
1517
1908
  if catalog_item_type_global_access != 'custom'
1518
1909
  print "\n", red, "Global Catalog Item Type Access is currently: #{catalog_item_type_global_access.to_s.capitalize}"
1519
1910
  print "\n", "You must first set it to Custom via `morpheus roles update-global-catalog-item-type-access \"#{name}\" custom`"
@@ -1588,7 +1979,7 @@ EOT
1588
1979
  opts.footer = "Update role access for a persona or all personas.\n" +
1589
1980
  "[role] is required. This is the name or id of a role.\n" +
1590
1981
  "--persona or --all is required. This is the code of a persona. Service Catalog, Standard, or Virtual Desktop\n" +
1591
- "--access is required. This is the new access value. #{anded_list(allowed_access_values)}"
1982
+ "--access is required. This is the new access value: #{ored_list(allowed_access_values)}"
1592
1983
  end
1593
1984
  optparse.parse!(args)
1594
1985
 
@@ -1726,7 +2117,7 @@ EOT
1726
2117
  opts.footer = "Update role access for a VDI pool or all VDI pools.\n" +
1727
2118
  "[role] is required. This is the name or id of a role.\n" +
1728
2119
  "--vdi-pool or --all is required. This is the name or id of a VDI pool.\n" +
1729
- "--access is required. This is the new access value. #{anded_list(allowed_access_values)}"
2120
+ "--access is required. This is the new access value: #{ored_list(allowed_access_values)}"
1730
2121
  end
1731
2122
  optparse.parse!(args)
1732
2123
 
@@ -1763,7 +2154,7 @@ EOT
1763
2154
 
1764
2155
  role_json = @roles_interface.get(account_id, role['id'])
1765
2156
  vdi_pool_global_access = role_json['globalVdiPoolAccess']
1766
- vdi_pool_permissions = role_json['vdiPoolPermissions'] || []
2157
+ vdi_pool_permissions = role_json['vdiPoolPermissions'] || role_json['vdiPools'] || []
1767
2158
  if vdi_pool_global_access != 'custom'
1768
2159
  print "\n", red, "Global VDI Pool Access is currently: #{vdi_pool_global_access.to_s.capitalize}"
1769
2160
  print "\n", "You must first set it to Custom via `morpheus roles update-global-vdi-pool-access \"#{name}\" custom`"
@@ -1885,7 +2276,7 @@ EOT
1885
2276
  opts.footer = "Update role access for a report type or all report types.\n" +
1886
2277
  "[role] is required. This is the name or id of a role.\n" +
1887
2278
  "--report-type or --all is required. This is the name or id of a report type.\n" +
1888
- "--access is required. This is the new access value. #{anded_list(allowed_access_values)}"
2279
+ "--access is required. This is the new access value: #{ored_list(allowed_access_values)}"
1889
2280
  end
1890
2281
  optparse.parse!(args)
1891
2282
 
@@ -1922,7 +2313,7 @@ EOT
1922
2313
 
1923
2314
  role_json = @roles_interface.get(account_id, role['id'])
1924
2315
  report_type_global_access = role_json['globalReportTypeAccess']
1925
- report_type_permissions = role_json['reportTypePermissions'] || []
2316
+ report_type_permissions = role_json['reportTypePermissions'] || role_json['reportTypes'] || []
1926
2317
  if report_type_global_access != 'custom'
1927
2318
  print "\n", red, "Global Report Type Access is currently: #{report_type_global_access.to_s.capitalize}"
1928
2319
  print "\n", "You must first set it to Custom via `morpheus roles update-global-report-type-access \"#{name}\" custom`"
@@ -1984,7 +2375,7 @@ EOT
1984
2375
  {'fieldName' => 'roleType', 'fieldLabel' => 'Role Type', 'type' => 'select', 'selectOptions' => [{'name' => 'User Role', 'value' => 'user'}, {'name' => 'Account Role', 'value' => 'account'}], 'defaultValue' => 'user'},
1985
2376
  {'fieldName' => 'baseRole', 'fieldLabel' => 'Copy From Role', 'type' => 'text'},
1986
2377
  {'fieldName' => 'multitenant', 'fieldLabel' => 'Multitenant', 'type' => 'checkbox', 'defaultValue' => 'off', 'description' => 'A Multitenant role is automatically copied into all existing subaccounts as well as placed into a subaccount when created. Useful for providing a set of predefined roles a Customer can use'},
1987
- {'fieldName' => 'multitenantLocked', 'fieldLabel' => 'Multitenant Locked', 'type' => 'checkbox', 'defaultValue' => 'off', 'description' => 'Prevents subtenants from branching off this role/modifying it. '},
2378
+ {'fieldName' => 'multitenantLocked', 'fieldLabel' => 'Multitenant Locked', 'type' => 'checkbox', 'defaultValue' => 'off', 'description' => 'Prevents subtenants from branching off this role/modifying it.'},
1988
2379
  {'fieldName' => 'defaultPersona', 'fieldLabel' => 'Default Persona', 'type' => 'select', 'selectOptions' => get_persona_select_options(), 'description' => 'Default Persona'}
1989
2380
  ]
1990
2381
  end
@@ -2005,4 +2396,18 @@ EOT
2005
2396
  ]
2006
2397
  end
2007
2398
 
2399
+ def parse_access_csv(output, val, args, optparse)
2400
+ output ||= {}
2401
+ val.split(",").each do |value_pair|
2402
+ # split on '=' only because ':' is included in the permission name
2403
+ k,v = value_pair.include?("=") ? value_pair.strip.split("=") : [value_pair, ""]
2404
+ k.strip!
2405
+ v.strip!
2406
+ if v == ""
2407
+ raise_command_error "permission '#{k}=#{v}' is invalid. The access code must be a value like [none|read|full]", args, optparse
2408
+ end
2409
+ output[k] = v
2410
+ end
2411
+ return output
2412
+ end
2008
2413
  end