morpheus-cli 9.0.1 → 9.0.2

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.
@@ -31,6 +31,8 @@ class Morpheus::Cli::Clusters
31
31
  register_subcommands :list_replicasets, :list_daemonsets, :list_endpoints, :list_ingresses, :list_policies, :list_volumes, :list_volume_claims, :list_config_maps, :list_secrets
32
32
  register_subcommands :get_pod, :get_deployment, :get_replicaset, :get_daemonset, :get_endpoint, :get_ingress, :get_policy, :get_volume_claim, :get_volume, :get_config_map, :get_secret, :get_stateful_set, :get_job, :get_service
33
33
  register_subcommands :list_affinity_groups, :get_affinity_group, :update_affinity_group, :add_affinity_group, :remove_affinity_group
34
+ register_subcommands :'list-affinity-group-violations' => :list_affinity_group_violations
35
+ register_subcommands :list_host_vm_groups, :get_host_vm_group, :add_host_vm_group, :update_host_vm_group, :remove_host_vm_group
34
36
 
35
37
  def connect(opts)
36
38
  @api_client = establish_remote_appliance_connection(opts)
@@ -3363,10 +3365,11 @@ class Morpheus::Cli::Clusters
3363
3365
  "ID" => 'id',
3364
3366
  "Name" => 'name',
3365
3367
  "Type" => lambda {|it| format_affinity_type(it['affinityType']) },
3368
+ "VM Group" => lambda {|it| it['vmGroup'] ? (it['vmGroup']['name'] || it['vmGroup']['id']) : '' },
3369
+ "Host Group" => lambda {|it| it['hostGroup'] ? (it['hostGroup']['name'] || it['hostGroup']['id']) : '' },
3366
3370
  "Resource Pool" => lambda {|it| it['pool'] ? (it['pool']['name'] || it['pool']['id']) : '' },
3367
3371
  "Visibility" => lambda {|it| it['visibility'].to_s.capitalize },
3368
- "Servers" => lambda {|it| it['servers'].size() },
3369
- # "Source" => lambda {|it| it['source'] },
3372
+ "Servers" => lambda {|it| (it['servers'] || []).size },
3370
3373
  }.upcase_keys!
3371
3374
  print as_pretty_table(affinity_groups, columns, options)
3372
3375
  print_results_pagination(json_response)
@@ -3414,14 +3417,18 @@ class Morpheus::Cli::Clusters
3414
3417
  "ID" => 'id',
3415
3418
  "Name" => 'name',
3416
3419
  "Type" => lambda {|it| format_affinity_type(it['affinityType']) },
3420
+ "Ref Type" => 'refType',
3421
+ "Ref ID" => 'refId',
3422
+ "VM Group" => lambda {|it| it['vmGroup'] ? (it['vmGroup']['name'] || it['vmGroup']['id']) : '' },
3423
+ "Host Group" => lambda {|it| it['hostGroup'] ? (it['hostGroup']['name'] || it['hostGroup']['id']) : '' },
3417
3424
  "Resource Pool" => lambda {|it| it['pool'] ? (it['pool']['name'] || it['pool']['id']) : '' },
3418
3425
  "Visibility" => lambda {|it| it['visibility'].to_s.capitalize },
3419
- "Servers" => lambda {|it| it['servers'].size() },
3426
+ "Servers" => lambda {|it| (it['servers'] || []).size },
3420
3427
  "Source" => lambda {|it| it['source'] },
3421
3428
  "Active" => lambda {|it| format_boolean(it['active']) },
3422
3429
  }
3423
3430
  print_description_list(columns, affinity_group)
3424
- if affinity_group['servers'].size > 0
3431
+ if affinity_group['servers'] && affinity_group['servers'].size > 0
3425
3432
  print_h2 "Servers", options
3426
3433
  print as_pretty_table(affinity_group['servers'], [:id, :name], options)
3427
3434
  end
@@ -3435,6 +3442,18 @@ class Morpheus::Cli::Clusters
3435
3442
  optparse = Morpheus::Cli::OptionParser.new do |opts|
3436
3443
  opts.banner = subcommand_usage( "[cluster] [name] [options]")
3437
3444
  build_option_type_options(opts, options, add_affinity_group_option_types)
3445
+ opts.on('--vm-group ID', String, "VM Group ID (for VM-to-Host rules)") do |val|
3446
+ options[:options] ||= {}
3447
+ options[:options]['vmGroup'] = {'id' => val.to_i}
3448
+ end
3449
+ opts.on('--host-group ID', String, "Host Group ID (for VM-to-Host rules)") do |val|
3450
+ options[:options] ||= {}
3451
+ options[:options]['hostGroup'] = {'id' => val.to_i}
3452
+ end
3453
+ opts.on('--servers ID1,ID2', Array, "Server IDs (for VM-to-VM rules)") do |val|
3454
+ options[:options] ||= {}
3455
+ options[:options]['servers'] = val.map {|s| {'id' => s.to_i}}
3456
+ end
3438
3457
  add_perms_options(opts, options, ['plans'])
3439
3458
  build_standard_add_options(opts, options)
3440
3459
  opts.footer = "Add affinity group to a cluster.\n" +
@@ -3450,6 +3469,7 @@ class Morpheus::Cli::Clusters
3450
3469
  cluster = find_cluster_by_name_or_id(args[0])
3451
3470
  return 1 if cluster.nil?
3452
3471
  if args[1]
3472
+ options[:options] ||= {}
3453
3473
  options[:options]['name'] = args[1]
3454
3474
  end
3455
3475
  if options[:payload]
@@ -3465,13 +3485,72 @@ class Morpheus::Cli::Clusters
3465
3485
  option_types = add_affinity_group_option_types
3466
3486
  affinity_group = Morpheus::Cli::OptionTypes.prompt(option_types, options[:options], @api_client, options[:params])
3467
3487
 
3468
- # affinity_group_type = find_affinity_group_type_by_code(affinity_group['affinityGroupType'])
3469
- # affinity_group['affinityGroupType'] = {id:affinity_group_type['id']}
3488
+ # Rule shape — servers OR vmGroup+hostGroup. Skip if already provided via CLI flags/-O or --payload merge.
3489
+ preset_servers = options[:options] && options[:options].key?('servers')
3490
+ preset_vmhost = options[:options] && options[:options].key?('vmGroup') && options[:options].key?('hostGroup')
3491
+ unless preset_servers || preset_vmhost
3492
+ form_opts = @clusters_interface.affinity_group_form_options(cluster['id']) rescue {}
3493
+ available_servers = form_opts['availableServers'] || []
3494
+ available_vm_groups = form_opts['availableVmGroups'] || []
3495
+ available_host_groups = form_opts['availableHostGroups'] || []
3496
+
3497
+ shape_options = [{'name' => 'VM-to-VM (pick servers)', 'value' => 'vmToVm'}]
3498
+ if !available_vm_groups.empty? && !available_host_groups.empty?
3499
+ shape_options << {'name' => 'VM-to-Host (pick groups)', 'value' => 'vmToHost'}
3500
+ end
3501
+ shape = if shape_options.size == 1
3502
+ 'vmToVm'
3503
+ else
3504
+ Morpheus::Cli::OptionTypes.prompt([{
3505
+ 'fieldName' => 'shape', 'fieldLabel' => 'Rule Shape', 'type' => 'select', 'required' => true,
3506
+ 'defaultValue' => 'vmToVm', 'selectOptions' => shape_options
3507
+ }], options[:options], @api_client, options[:params])['shape']
3508
+ end
3470
3509
 
3471
- # # affinity_group type options
3472
- # unless affinity_group_type['optionTypes'].empty?
3473
- # affinity_group.merge!(Morpheus::Cli::OptionTypes.prompt(affinity_group_type['optionTypes'], options[:options].deep_merge({:context_map => {'domain' => ''}, :checkbox_as_boolean => true}), @api_client, options[:params]))
3474
- # end
3510
+ if shape == 'vmToVm'
3511
+ if available_servers.empty?
3512
+ # Fall back to the classic multiTypeahead prompt when the form-options endpoint returns nothing
3513
+ server_prompt = Morpheus::Cli::OptionTypes.prompt([
3514
+ {'fieldName' => 'servers', 'fieldLabel' => 'Server', 'type' => 'multiTypeahead', 'optionSource' => 'searchServers', 'searchParameter' => 'phrase', 'description' => 'Select servers to be in the affinity group.'}
3515
+ ], options[:options], @api_client, options[:params])
3516
+ affinity_group['servers'] = server_prompt['servers'] if server_prompt['servers']
3517
+ else
3518
+ print cyan, "Available Servers:", reset, "\n"
3519
+ available_servers.each {|s| print " #{s['id']}\t#{s['name']}\n" }
3520
+ raw = Morpheus::Cli::OptionTypes.prompt([{
3521
+ 'fieldName' => 'servers', 'fieldLabel' => 'Servers (comma-separated IDs)', 'type' => 'text', 'required' => true,
3522
+ 'description' => 'Server IDs to include, comma-separated'
3523
+ }], options[:options], @api_client, options[:params])['servers']
3524
+ selected_ids = raw.to_s.split(',').map(&:strip).reject(&:empty?).map(&:to_i)
3525
+ valid_ids = available_servers.map {|s| s['id'].to_i}
3526
+ invalid = selected_ids - valid_ids
3527
+ if !invalid.empty?
3528
+ print_red_alert "Invalid server IDs for this cluster: #{invalid.join(', ')}"
3529
+ return 1
3530
+ end
3531
+ if selected_ids.empty?
3532
+ print_red_alert "At least one server ID is required"
3533
+ return 1
3534
+ end
3535
+ affinity_group['servers'] = selected_ids.map {|id| {'id' => id}}
3536
+ end
3537
+ else
3538
+ vm_group_id = Morpheus::Cli::OptionTypes.prompt([{
3539
+ 'fieldName' => 'vmGroup', 'fieldLabel' => 'VM Group', 'type' => 'select', 'required' => true,
3540
+ 'selectOptions' => available_vm_groups.map {|g| {'name' => g['name'], 'value' => g['id']} }
3541
+ }], options[:options], @api_client, options[:params])['vmGroup']
3542
+ host_group_id = Morpheus::Cli::OptionTypes.prompt([{
3543
+ 'fieldName' => 'hostGroup', 'fieldLabel' => 'Host Group', 'type' => 'select', 'required' => true,
3544
+ 'selectOptions' => available_host_groups.map {|g| {'name' => g['name'], 'value' => g['id']} }
3545
+ }], options[:options], @api_client, options[:params])['hostGroup']
3546
+ affinity_group['vmGroup'] = {'id' => vm_group_id.to_i}
3547
+ affinity_group['hostGroup'] = {'id' => host_group_id.to_i}
3548
+ end
3549
+ else
3550
+ affinity_group['servers'] = options[:options]['servers'] if preset_servers
3551
+ affinity_group['vmGroup'] = options[:options]['vmGroup'] if preset_vmhost
3552
+ affinity_group['hostGroup'] = options[:options]['hostGroup'] if preset_vmhost
3553
+ end
3475
3554
 
3476
3555
  # perms
3477
3556
  perms = prompt_permissions(options, is_master_account ? ['plans'] : ['plans', 'visibility', 'tenants'])
@@ -3659,6 +3738,367 @@ class Morpheus::Cli::Clusters
3659
3738
  return 0, nil
3660
3739
  end
3661
3740
 
3741
+ def list_affinity_group_violations(args)
3742
+ options = {}
3743
+ params = {}
3744
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
3745
+ opts.banner = subcommand_usage("[cluster]")
3746
+ build_standard_list_options(opts, options)
3747
+ opts.footer = "List active MUST-rule affinity violations.\n" +
3748
+ "[cluster] is required. This is the name or id of an existing cluster."
3749
+ end
3750
+ optparse.parse!(args)
3751
+ verify_args!(args:args, optparse:optparse, count:1)
3752
+ connect(options)
3753
+ cluster = find_cluster_by_name_or_id(args[0])
3754
+ return 1 if cluster.nil?
3755
+ params['clusterId'] = cluster['id']
3756
+ params.merge!(parse_list_options(options))
3757
+ @clusters_interface.setopts(options)
3758
+ if options[:dry_run]
3759
+ print_dry_run @clusters_interface.dry.affinity_group_violations(params)
3760
+ return
3761
+ end
3762
+ json_response = @clusters_interface.affinity_group_violations(params)
3763
+ render_response(json_response, options, 'violations') do
3764
+ rows = json_response['violations'] || []
3765
+ print_h1 "Affinity Rule Violations", parse_list_subtitles(options), options
3766
+ if rows.empty?
3767
+ print cyan, "No violations.", reset, "\n"
3768
+ else
3769
+ columns = {
3770
+ "Rule" => 'ruleName',
3771
+ "Type" => lambda {|r| r['keepTogether'] ? 'Keep Together' : 'Keep Separate' },
3772
+ "VMs" => lambda {|r| (r['affectedVms'] || []).map {|v| v['name']}.join(', ') },
3773
+ "Occurred" => 'occurredAt'
3774
+ }.upcase_keys!
3775
+ print as_pretty_table(rows, columns, options)
3776
+ end
3777
+ print reset, "\n"
3778
+ end
3779
+ return 0, nil
3780
+ end
3781
+
3782
+ def list_host_vm_groups(args)
3783
+ options = {}
3784
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
3785
+ opts.banner = subcommand_usage("[cluster]")
3786
+ build_standard_list_options(opts, options)
3787
+ opts.footer = "List host/VM groups on a cluster.\n" +
3788
+ "[cluster] is required. This is the name or id of an existing cluster."
3789
+ end
3790
+ optparse.parse!(args)
3791
+ verify_args!(args:args, optparse:optparse, count:1)
3792
+ connect(options)
3793
+ cluster = find_cluster_by_name_or_id(args[0])
3794
+ return 1 if cluster.nil?
3795
+ params = {}
3796
+ params.merge!(parse_list_options(options))
3797
+ @clusters_interface.setopts(options)
3798
+ if options[:dry_run]
3799
+ print_dry_run @clusters_interface.dry.list_host_vm_groups(cluster['id'], params)
3800
+ return
3801
+ end
3802
+ json_response = @clusters_interface.list_host_vm_groups(cluster['id'], params)
3803
+ render_response(json_response, options, 'hostVmGroups') do
3804
+ rows = json_response['hostVmGroups'] || []
3805
+ print_h1 "Morpheus Cluster Host/VM Groups: #{cluster['name']}", parse_list_subtitles(options), options
3806
+ if rows.empty?
3807
+ print cyan, "No host/VM groups found.", reset, "\n"
3808
+ else
3809
+ columns = {
3810
+ "ID" => 'id',
3811
+ "Name" => 'name',
3812
+ "Type" => lambda {|it| format_host_vm_group_type(it['type']) },
3813
+ "Servers" => lambda {|it| (it['servers'] || []).size },
3814
+ "Visibility" => lambda {|it| it['visibility'].to_s.capitalize },
3815
+ }.upcase_keys!
3816
+ print as_pretty_table(rows, columns, options)
3817
+ print_results_pagination(json_response)
3818
+ end
3819
+ print reset, "\n"
3820
+ end
3821
+ return 0, nil
3822
+ end
3823
+
3824
+ def get_host_vm_group(args)
3825
+ options = {}
3826
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
3827
+ opts.banner = subcommand_usage("[cluster] [host-vm-group]")
3828
+ build_standard_get_options(opts, options)
3829
+ opts.footer = "Get details about a cluster host/VM group.\n" +
3830
+ "[cluster] is required. This is the name or id of an existing cluster.\n" +
3831
+ "[host-vm-group] is required. This is the name or id of an existing host/VM group."
3832
+ end
3833
+ optparse.parse!(args)
3834
+ verify_args!(args:args, optparse:optparse, count:2)
3835
+ connect(options)
3836
+ cluster = find_cluster_by_name_or_id(args[0])
3837
+ return 1 if cluster.nil?
3838
+ host_vm_group = find_cluster_host_vm_group_by_name_or_id(cluster['id'], args[1])
3839
+ if host_vm_group.nil?
3840
+ print_red_alert "Host/VM Group not found for '#{args[1]}'"
3841
+ exit 1
3842
+ end
3843
+ params = {}
3844
+ params.merge!(parse_query_options(options))
3845
+ @clusters_interface.setopts(options)
3846
+ if options[:dry_run]
3847
+ print_dry_run @clusters_interface.dry.get_host_vm_group(cluster['id'], host_vm_group['id'], params)
3848
+ return
3849
+ end
3850
+ json_response = @clusters_interface.get_host_vm_group(cluster['id'], host_vm_group['id'], params)
3851
+ render_response(json_response, options, 'hostVmGroup') do
3852
+ row = json_response['hostVmGroup']
3853
+ print_h1 "Host/VM Group Details", [], options
3854
+ columns = {
3855
+ "ID" => 'id',
3856
+ "Name" => 'name',
3857
+ "Type" => lambda {|it| format_host_vm_group_type(it['type']) },
3858
+ "Ref Type" => 'refType',
3859
+ "Ref ID" => 'refId',
3860
+ "Servers" => lambda {|it| (it['servers'] || []).size },
3861
+ "Visibility" => lambda {|it| it['visibility'].to_s.capitalize },
3862
+ "Active" => lambda {|it| format_boolean(it['active']) },
3863
+ }
3864
+ print_description_list(columns, row)
3865
+ if row['servers'] && row['servers'].size > 0
3866
+ print_h2 "Servers", options
3867
+ print as_pretty_table(row['servers'], [:id, :name], options)
3868
+ end
3869
+ print reset, "\n"
3870
+ end
3871
+ return 0, nil
3872
+ end
3873
+
3874
+ def add_host_vm_group(args)
3875
+ options = {}
3876
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
3877
+ opts.banner = subcommand_usage("[cluster] [name] [options]")
3878
+ build_option_type_options(opts, options, add_host_vm_group_option_types)
3879
+ opts.on('--servers ID1,ID2', Array, "Server IDs to include") do |val|
3880
+ options[:options] ||= {}
3881
+ options[:options]['servers'] = val.map {|s| {'id' => s.to_i}}
3882
+ end
3883
+ add_perms_options(opts, options, ['plans'])
3884
+ build_standard_add_options(opts, options)
3885
+ opts.footer = "Add a host/VM group to a cluster.\n" +
3886
+ "[cluster] is required. This is the name or id of an existing cluster.\n" +
3887
+ "[name] is required. This is the name of the new host/VM group."
3888
+ end
3889
+ optparse.parse!(args)
3890
+ verify_args!(args:args, optparse:optparse, min:1, max:2)
3891
+ connect(options)
3892
+
3893
+ begin
3894
+ cluster = find_cluster_by_name_or_id(args[0])
3895
+ return 1 if cluster.nil?
3896
+ if args[1]
3897
+ options[:options] ||= {}
3898
+ options[:options]['name'] = args[1]
3899
+ end
3900
+ if options[:payload]
3901
+ payload = options[:payload]
3902
+ if options[:options]
3903
+ payload ||= {}
3904
+ payload.deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) })
3905
+ end
3906
+ else
3907
+ options[:params] ||= {}
3908
+ options[:params].merge!({:serverGroupId => cluster['id']})
3909
+ option_types = add_host_vm_group_option_types
3910
+ host_vm_group = Morpheus::Cli::OptionTypes.prompt(option_types, options[:options], @api_client, options[:params])
3911
+
3912
+ # Servers — form-options driven, filtered by chosen type
3913
+ unless options[:options] && options[:options].key?('servers')
3914
+ form_opts = @clusters_interface.host_vm_group_form_options(cluster['id'], {'hostVmGroupType' => host_vm_group['type']}) rescue {}
3915
+ available_servers = form_opts['availableServers'] || []
3916
+ if available_servers.empty?
3917
+ print_red_alert "No servers available on cluster #{cluster['name']} for type #{host_vm_group['type']}"
3918
+ return 1
3919
+ end
3920
+ print cyan, "Available Servers:", reset, "\n"
3921
+ available_servers.each {|s| print " #{s['id']}\t#{s['name']}\n" }
3922
+ raw = Morpheus::Cli::OptionTypes.prompt([{
3923
+ 'fieldName' => 'servers', 'fieldLabel' => 'Servers (comma-separated IDs)', 'type' => 'text', 'required' => true,
3924
+ 'description' => 'Server IDs to include, comma-separated'
3925
+ }], options[:options], @api_client, options[:params])['servers']
3926
+ selected_ids = raw.to_s.split(',').map(&:strip).reject(&:empty?).map(&:to_i)
3927
+ valid_ids = available_servers.map {|s| s['id'].to_i}
3928
+ invalid = selected_ids - valid_ids
3929
+ if !invalid.empty?
3930
+ print_red_alert "Invalid server IDs for this cluster/type: #{invalid.join(', ')}"
3931
+ return 1
3932
+ end
3933
+ if selected_ids.empty?
3934
+ print_red_alert "At least one server ID is required"
3935
+ return 1
3936
+ end
3937
+ host_vm_group['servers'] = selected_ids.map {|id| {'id' => id}}
3938
+ else
3939
+ host_vm_group['servers'] = options[:options]['servers']
3940
+ end
3941
+
3942
+ perms = prompt_permissions(options, is_master_account ? ['plans'] : ['plans', 'visibility', 'tenants'])
3943
+ host_vm_group['resourcePermissions'] = perms['resourcePermissions'] unless perms['resourcePermissions'].nil?
3944
+ host_vm_group['tenants'] = perms['tenantPermissions'] unless perms['tenantPermissions'].nil? || perms['tenantPermissions']['accounts'].empty?
3945
+ host_vm_group['visibility'] = perms['resourcePool']['visibility'] if !perms['resourcePool'].nil? && !perms['resourcePool']['visibility'].nil?
3946
+
3947
+ payload = {'hostVmGroup' => host_vm_group}
3948
+ end
3949
+
3950
+ @clusters_interface.setopts(options)
3951
+ if options[:dry_run]
3952
+ print_dry_run @clusters_interface.dry.create_host_vm_group(cluster['id'], payload)
3953
+ return
3954
+ end
3955
+ json_response = @clusters_interface.create_host_vm_group(cluster['id'], payload)
3956
+ if options[:json]
3957
+ puts as_json(json_response)
3958
+ else
3959
+ if json_response['success']
3960
+ print_green_success (json_response['msg'] || "Added host/VM group to cluster #{cluster['name']}")
3961
+ else
3962
+ print_red_alert "Failed to create host/VM group #{json_response['msg']}"
3963
+ end
3964
+ end
3965
+ return 0
3966
+ rescue RestClient::Exception => e
3967
+ print_rest_exception(e, options)
3968
+ exit 1
3969
+ end
3970
+ end
3971
+
3972
+ def update_host_vm_group(args)
3973
+ options = {}
3974
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
3975
+ opts.banner = subcommand_usage("[cluster] [host-vm-group] [options]")
3976
+ build_option_type_options(opts, options, update_host_vm_group_option_types)
3977
+ opts.on('--servers ID1,ID2', Array, "Replace server IDs") do |val|
3978
+ options[:options] ||= {}
3979
+ options[:options]['servers'] = val.map {|s| {'id' => s.to_i}}
3980
+ end
3981
+ opts.on('--visibility VISIBILITY', String, "Visibility: private | public") do |val|
3982
+ options[:options] ||= {}
3983
+ options[:options]['visibility'] = val
3984
+ end
3985
+ add_perms_options(opts, options, ['plans'])
3986
+ build_standard_update_options(opts, options)
3987
+ opts.footer = "Update a cluster host/VM group.\n" +
3988
+ "[cluster] is required. This is the name or id of an existing cluster.\n" +
3989
+ "[host-vm-group] is required. This is the name or id of an existing host/VM group."
3990
+ end
3991
+ optparse.parse!(args)
3992
+ if args.count != 2
3993
+ raise_command_error "wrong number of arguments, expected 2 and got (#{args.count}) #{args}\n#{optparse}"
3994
+ end
3995
+ connect(options)
3996
+
3997
+ begin
3998
+ cluster = find_cluster_by_name_or_id(args[0])
3999
+ return 1 if cluster.nil?
4000
+ host_vm_group = find_cluster_host_vm_group_by_name_or_id(cluster['id'], args[1])
4001
+ if host_vm_group.nil?
4002
+ print_red_alert "Host/VM Group not found by '#{args[1]}'"
4003
+ exit 1
4004
+ end
4005
+ payload = nil
4006
+ if options[:payload]
4007
+ payload = options[:payload]
4008
+ if options[:options]
4009
+ payload.deep_merge!({'hostVmGroup' => options[:options].reject {|k,v| k.is_a?(Symbol) }})
4010
+ end
4011
+ else
4012
+ payload = {'hostVmGroup' => {}}
4013
+ options[:params] ||= {}
4014
+ options[:params].merge!({:serverGroupId => cluster['id']})
4015
+ option_types = update_host_vm_group_option_types
4016
+ v_prompt = Morpheus::Cli::OptionTypes.no_prompt(option_types, options[:options], @api_client, options[:params])
4017
+ v_prompt.deep_compact!.booleanize!
4018
+ payload.deep_merge!({'hostVmGroup' => v_prompt})
4019
+
4020
+ perms = prompt_permissions(options.merge({:no_prompt => true}), is_master_account ? ['plans'] : ['plans', 'visibility', 'tenants'])
4021
+ perms_payload = {}
4022
+ perms_payload['resourcePermissions'] = perms['resourcePermissions'] if !perms['resourcePermissions'].nil? && !perms['resourcePermissions'].empty?
4023
+ perms_payload['tenantPermissions'] = perms['tenantPermissions'] if !perms['tenantPermissions'].nil? && !perms['tenantPermissions']['accounts'].empty?
4024
+ payload['hostVmGroup']['permissions'] = perms_payload if !perms_payload.empty?
4025
+ payload['hostVmGroup']['visibility'] = perms['resourcePool']['visibility'] if !perms['resourcePool'].nil? && !perms['resourcePool']['visibility'].nil?
4026
+
4027
+ if options[:options]
4028
+ payload.deep_merge!({'hostVmGroup' => options[:options].reject {|k,v| k.is_a?(Symbol) || payload['hostVmGroup'].key?(k) }})
4029
+ end
4030
+
4031
+ if payload['hostVmGroup'].nil? || payload['hostVmGroup'].empty?
4032
+ raise_command_error "Specify at least one option to update.\n#{optparse}"
4033
+ end
4034
+ end
4035
+
4036
+ @clusters_interface.setopts(options)
4037
+ if options[:dry_run]
4038
+ print_dry_run @clusters_interface.dry.update_host_vm_group(cluster['id'], host_vm_group['id'], payload)
4039
+ return
4040
+ end
4041
+ json_response = @clusters_interface.update_host_vm_group(cluster['id'], host_vm_group['id'], payload)
4042
+ if options[:json]
4043
+ puts as_json(json_response)
4044
+ elsif !options[:quiet]
4045
+ updated = json_response['hostVmGroup'] || host_vm_group
4046
+ print_green_success "Updated host/VM group #{updated['name'] || updated['id']}"
4047
+ end
4048
+ return 0
4049
+ rescue RestClient::Exception => e
4050
+ print_rest_exception(e, options)
4051
+ exit 1
4052
+ end
4053
+ end
4054
+
4055
+ def remove_host_vm_group(args)
4056
+ params = {}
4057
+ options = {}
4058
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
4059
+ opts.banner = subcommand_usage("[cluster] [host-vm-group]")
4060
+ build_standard_remove_options(opts, options)
4061
+ opts.footer = "Delete a host/VM group from a cluster.\n" +
4062
+ "[cluster] is required. This is the name or id of an existing cluster.\n" +
4063
+ "[host-vm-group] is required. This is the name or id of an existing host/VM group."
4064
+ end
4065
+ optparse.parse!(args)
4066
+ verify_args!(args:args, optparse:optparse, count:2)
4067
+ connect(options)
4068
+ params.merge!(parse_query_options(options))
4069
+
4070
+ cluster = find_cluster_by_name_or_id(args[0])
4071
+ return 1 if cluster.nil?
4072
+ host_vm_group_id = args[1]
4073
+ if host_vm_group_id.empty?
4074
+ raise_command_error "missing required host/VM group parameter"
4075
+ end
4076
+ host_vm_group = find_cluster_host_vm_group_by_name_or_id(cluster['id'], host_vm_group_id)
4077
+ if host_vm_group.nil?
4078
+ print_red_alert "Host/VM Group not found for '#{host_vm_group_id}'"
4079
+ return 1
4080
+ end
4081
+ unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to remove the cluster host/VM group '#{host_vm_group['name'] || host_vm_group['id']}'?", options)
4082
+ return 9, "aborted command"
4083
+ end
4084
+ @clusters_interface.setopts(options)
4085
+ if options[:dry_run]
4086
+ print_dry_run @clusters_interface.dry.destroy_host_vm_group(cluster['id'], host_vm_group['id'], params)
4087
+ return
4088
+ end
4089
+ json_response = @clusters_interface.destroy_host_vm_group(cluster['id'], host_vm_group['id'], params)
4090
+ if options[:json]
4091
+ puts as_json(json_response)
4092
+ else
4093
+ if json_response['success']
4094
+ print_green_success "Removed host/VM group #{host_vm_group['name']}"
4095
+ else
4096
+ print_red_alert "Failed to remove cluster host/VM group #{json_response['msg']}"
4097
+ end
4098
+ end
4099
+ return 0, nil
4100
+ end
4101
+
3662
4102
  def api_config(args)
3663
4103
  options = {}
3664
4104
  optparse = Morpheus::Cli::OptionParser.new do |opts|
@@ -5346,22 +5786,71 @@ class Morpheus::Cli::Clusters
5346
5786
  def add_affinity_group_option_types
5347
5787
  [
5348
5788
  {'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true},
5349
- {'fieldName' => 'affinityType', 'fieldLabel' => 'Type', 'type' => 'select', 'selectOptions' => [{'name' => 'Keep Separate', 'value' => 'KEEP_SEPARATE'}, {'name' => 'Keep Together', 'value' => 'KEEP_TOGETHER'}], 'description' => 'Choose affinity type.', 'required' => true, 'defaultValue' => 'KEEP_SEPARATE'},
5789
+ {'fieldName' => 'affinityType', 'fieldLabel' => 'Type', 'type' => 'select', 'selectOptions' => [
5790
+ {'name' => 'Keep Together (Should)', 'value' => 'KEEP_TOGETHER'},
5791
+ {'name' => 'Keep Separate (Should)', 'value' => 'KEEP_SEPARATE'},
5792
+ {'name' => 'Keep Together (Must)', 'value' => 'KEEP_TOGETHER_MUST'},
5793
+ {'name' => 'Keep Separate (Must)', 'value' => 'KEEP_SEPARATE_MUST'}
5794
+ ], 'description' => 'Choose affinity type.', 'required' => true, 'defaultValue' => 'KEEP_TOGETHER'},
5350
5795
  {'fieldName' => 'active', 'fieldLabel' => 'Active', 'type' => 'checkbox', 'defaultValue' => true},
5351
- # {'fieldName' => 'pool.id', 'fieldLabel' => 'Cluster', 'type' => 'select', 'optionSourceType' => 'vmware', 'optionSource' => 'vmwareZonePoolClusters', 'description' => 'Select cluster for the affinity group.', 'required' => true},
5352
- {'fieldName' => 'servers', 'fieldLabel' => 'Server', 'type' => 'multiTypeahead', 'optionSource' => 'searchServers', 'searchParameter' => 'phrase', 'description' => 'Select servers to be in the affinity group.'},
5353
5796
  ]
5354
5797
  end
5355
5798
 
5356
5799
  def update_affinity_group_option_types
5357
5800
  [
5801
+ {'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text'},
5802
+ {'fieldName' => 'affinityType', 'fieldLabel' => 'Type', 'type' => 'select', 'selectOptions' => [
5803
+ {'name' => 'Keep Together (Should)', 'value' => 'KEEP_TOGETHER'},
5804
+ {'name' => 'Keep Separate (Should)', 'value' => 'KEEP_SEPARATE'},
5805
+ {'name' => 'Keep Together (Must)', 'value' => 'KEEP_TOGETHER_MUST'},
5806
+ {'name' => 'Keep Separate (Must)', 'value' => 'KEEP_SEPARATE_MUST'}
5807
+ ], 'description' => 'Change affinity type.'},
5358
5808
  {'fieldName' => 'active', 'fieldLabel' => 'Active', 'type' => 'checkbox'},
5359
5809
  {'fieldName' => 'servers', 'fieldLabel' => 'Server', 'type' => 'multiTypeahead', 'optionSource' => 'searchServers', 'searchParameter' => 'phrase', 'description' => 'Select servers to be in the affinity group.'},
5360
5810
  ]
5361
5811
  end
5362
5812
 
5363
5813
  def format_affinity_type(affinity_type)
5364
- affinity_type == "KEEP_SEPARATE" ? "Keep Separate" : "Keep Together"
5814
+ case affinity_type.to_s
5815
+ when 'KEEP_SEPARATE' then 'Keep Separate'
5816
+ when 'KEEP_TOGETHER' then 'Keep Together'
5817
+ when 'KEEP_SEPARATE_MUST' then 'Keep Separate (Must)'
5818
+ when 'KEEP_TOGETHER_MUST' then 'Keep Together (Must)'
5819
+ else affinity_type.to_s
5820
+ end
5821
+ end
5822
+
5823
+ def add_host_vm_group_option_types
5824
+ [
5825
+ {'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true},
5826
+ {'fieldName' => 'type', 'fieldLabel' => 'Group Type', 'type' => 'select', 'selectOptions' => [
5827
+ {'name' => 'Host Group', 'value' => 'HOST_GROUP'},
5828
+ {'name' => 'VM Group', 'value' => 'VM_GROUP'}
5829
+ ], 'required' => true, 'defaultValue' => 'HOST_GROUP', 'description' => 'Choose group type.'},
5830
+ ]
5831
+ end
5832
+
5833
+ def update_host_vm_group_option_types
5834
+ [
5835
+ {'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text'},
5836
+ ]
5837
+ end
5838
+
5839
+ def format_host_vm_group_type(type)
5840
+ case type.to_s
5841
+ when 'HOST_GROUP' then 'Host Group'
5842
+ when 'VM_GROUP' then 'VM Group'
5843
+ else type.to_s
5844
+ end
5845
+ end
5846
+
5847
+ def find_cluster_host_vm_group_by_name_or_id(cluster_id, val)
5848
+ if val.to_s =~ /\A\d{1,}\Z/
5849
+ @clusters_interface.get_host_vm_group(cluster_id, val)['hostVmGroup'] rescue nil
5850
+ else
5851
+ list = @clusters_interface.list_host_vm_groups(cluster_id, {name: val})['hostVmGroups'] || []
5852
+ list.first
5853
+ end
5365
5854
  end
5366
5855
 
5367
5856
  end