morpheus-cli 5.5.0 → 5.5.1

Sign up to get free protection for your applications and to get access to all the features.
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