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.
- checksums.yaml +4 -4
- data/Dockerfile +1 -1
- data/lib/morpheus/api/api_client.rb +4 -0
- data/lib/morpheus/api/clusters_interface.rb +48 -0
- data/lib/morpheus/api/servers_interface.rb +8 -0
- data/lib/morpheus/api/virtual_switches_interface.rb +9 -0
- data/lib/morpheus/cli/commands/backups_command.rb +11 -0
- data/lib/morpheus/cli/commands/clusters.rb +503 -14
- data/lib/morpheus/cli/commands/hosts.rb +72 -1
- data/lib/morpheus/cli/commands/instances.rb +12 -1
- data/lib/morpheus/cli/commands/networks_command.rb +35 -3
- data/lib/morpheus/cli/commands/virtual_switches_command.rb +336 -0
- data/lib/morpheus/cli/mixins/infrastructure_helper.rb +6 -4
- data/lib/morpheus/cli/version.rb +1 -1
- metadata +4 -2
|
@@ -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
|
-
#
|
|
3469
|
-
|
|
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
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
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' => [
|
|
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
|
|
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
|