morpheus-cli 8.0.6 → 8.0.8

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.
@@ -28,6 +28,7 @@ class Morpheus::Cli::Clusters
28
28
  register_subcommands :refresh
29
29
  register_subcommands :list_replicasets, :list_daemonsets, :list_endpoints, :list_ingresses, :list_policies, :list_volumes, :list_volume_claims, :list_config_maps, :list_secrets
30
30
  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
31
+ register_subcommands :list_affinity_groups, :get_affinity_group, :update_affinity_group, :add_affinity_group, :remove_affinity_group
31
32
 
32
33
  def connect(opts)
33
34
  @api_client = establish_remote_appliance_connection(opts)
@@ -1379,7 +1380,12 @@ class Morpheus::Cli::Clusters
1379
1380
  (['provisionType.vmware.host', 'provisionType.scvmm.host'].include?(type['code']) && cloud['config']['hideHostSelection'] == 'on') || # should this be truthy?
1380
1381
  (type['fieldContext'] == 'instance.networkDomain' && type['fieldName'] == 'id')
1381
1382
  } rescue [])
1382
-
1383
+ # strip server context
1384
+ option_type_list.each do |option_type|
1385
+ if option_type['fieldContext'] == 'server' || option_type['fieldContext'] == 'domain'
1386
+ option_type['fieldContext'] = nil
1387
+ end
1388
+ end
1383
1389
  # remove metadata option_type , prompt manually for that field 'tags' instead of 'metadata'
1384
1390
  #metadata_option_type = option_type_list.find {|type| type['fieldName'] == 'metadata' }
1385
1391
  metadata_option_type = cluster_type['optionTypes'].find {|type| type['fieldName'] == 'metadata' }
@@ -3269,6 +3275,337 @@ class Morpheus::Cli::Clusters
3269
3275
  end
3270
3276
  end
3271
3277
 
3278
+ def list_affinity_groups(args)
3279
+ options = {}
3280
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
3281
+ opts.banner = subcommand_usage( "[cluster]")
3282
+ build_standard_list_options(opts, options)
3283
+ opts.footer = "List affinity groups for a cluster.\n" +
3284
+ "[cluster] is required. This is the name or id of an existing cluster."
3285
+ end
3286
+
3287
+ optparse.parse!(args)
3288
+ verify_args!(args:args, optparse:optparse, count:1)
3289
+ connect(options)
3290
+
3291
+ cluster = find_cluster_by_name_or_id(args[0])
3292
+ return 1 if cluster.nil?
3293
+
3294
+ params = {}
3295
+ params.merge!(parse_list_options(options))
3296
+ @clusters_interface.setopts(options)
3297
+ if options[:dry_run]
3298
+ print_dry_run @clusters_interface.dry.list_affinity_groups(cluster['id'], params)
3299
+ return
3300
+ end
3301
+ json_response = @clusters_interface.list_affinity_groups(cluster['id'], params)
3302
+ render_response(json_response, options, 'affinityGroups') do
3303
+ affinity_groups = json_response['affinityGroups']
3304
+ print_h1 "Morpheus Cluster Affinity Groups: #{cluster['name']}", parse_list_subtitles(options), options
3305
+ if affinity_groups.empty?
3306
+ print cyan,"No affinity groups found.",reset,"\n"
3307
+ else
3308
+ columns = {
3309
+ "ID" => 'id',
3310
+ "Name" => 'name',
3311
+ "Type" => lambda {|it| format_affinity_type(it['affinityType']) },
3312
+ "Resource Pool" => lambda {|it| it['pool'] ? (it['pool']['name'] || it['pool']['id']) : '' },
3313
+ "Visibility" => lambda {|it| it['visibility'].to_s.capitalize },
3314
+ "Servers" => lambda {|it| it['servers'].size() },
3315
+ # "Source" => lambda {|it| it['source'] },
3316
+ }.upcase_keys!
3317
+ print as_pretty_table(affinity_groups, columns, options)
3318
+ print_results_pagination(json_response)
3319
+ end
3320
+ print reset,"\n"
3321
+ end
3322
+ return 0, nil
3323
+ end
3324
+
3325
+ def get_affinity_group(args)
3326
+ options = {}
3327
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
3328
+ opts.banner = subcommand_usage( "[cluster] [affinity group]")
3329
+ build_standard_get_options(opts, options)
3330
+ opts.footer = "Get details about a cluster affinity group.\n" +
3331
+ "[cluster] is required. This is the name or id of an existing cluster.\n" +
3332
+ "[affinity group] is required. This is the name or id of an existing affinity group."
3333
+ end
3334
+ optparse.parse!(args)
3335
+ verify_args!(args:args, optparse:optparse, count:2)
3336
+ connect(options)
3337
+
3338
+ cluster = find_cluster_by_name_or_id(args[0])
3339
+ return 1 if cluster.nil?
3340
+
3341
+ # this finds the affinity group in the cluster api response, then fetches it by ID
3342
+ affinity_group = find_cluster_affinity_group_by_name_or_id(cluster['id'], args[1])
3343
+ if affinity_group.nil?
3344
+ print_red_alert "Affinity Group not found for '#{args[1]}'"
3345
+ exit 1
3346
+ end
3347
+
3348
+ params = {}
3349
+ params.merge!(parse_query_options(options))
3350
+ @clusters_interface.setopts(options)
3351
+ if options[:dry_run]
3352
+ print_dry_run @clusters_interface.dry.get_affinity_group(cluster['id'], affinity_group['id'], params)
3353
+ return
3354
+ end
3355
+ json_response = @clusters_interface.get_affinity_group(cluster['id'], affinity_group['id'], params)
3356
+ render_response(json_response, options, 'affinityGroup') do
3357
+ affinity_group = json_response['affinityGroup']
3358
+ print_h1 "Affinity Group Details", [], options
3359
+ columns = {
3360
+ "ID" => 'id',
3361
+ "Name" => 'name',
3362
+ "Type" => lambda {|it| format_affinity_type(it['affinityType']) },
3363
+ "Resource Pool" => lambda {|it| it['pool'] ? (it['pool']['name'] || it['pool']['id']) : '' },
3364
+ "Visibility" => lambda {|it| it['visibility'].to_s.capitalize },
3365
+ "Servers" => lambda {|it| it['servers'].size() },
3366
+ "Source" => lambda {|it| it['source'] },
3367
+ "Active" => lambda {|it| format_boolean(it['active']) },
3368
+ }
3369
+ print_description_list(columns, affinity_group)
3370
+ if affinity_group['servers'].size > 0
3371
+ print_h2 "Servers", options
3372
+ print as_pretty_table(affinity_group['servers'], [:id, :name], options)
3373
+ end
3374
+ print reset,"\n"
3375
+ end
3376
+ return 0, nil
3377
+ end
3378
+
3379
+ def add_affinity_group(args)
3380
+ options = {}
3381
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
3382
+ opts.banner = subcommand_usage( "[cluster] [name] [options]")
3383
+ build_option_type_options(opts, options, add_affinity_group_option_types)
3384
+ # opts.on('--refresh [SECONDS]', String, "Refresh until execution is complete. Default interval is #{default_refresh_interval} seconds.") do |val|
3385
+ # options[:refresh_interval] = val.to_s.empty? ? default_refresh_interval : val.to_f
3386
+ # end
3387
+ # opts.on(nil, '--no-refresh', "Do not refresh" ) do
3388
+ # options[:no_refresh] = true
3389
+ # end
3390
+ build_standard_add_options(opts, options)
3391
+ opts.footer = "Add affinity group to a cluster.\n" +
3392
+ "[cluster] is required. This is the name or id of an existing cluster.\n" +
3393
+ "[name] is required. This is the name of the new affinity group."
3394
+ end
3395
+
3396
+ optparse.parse!(args)
3397
+ verify_args!(args:args, optparse:optparse, min:1, max:2)
3398
+ connect(options)
3399
+
3400
+ begin
3401
+ cluster = find_cluster_by_name_or_id(args[0])
3402
+ return 1 if cluster.nil?
3403
+ if args[1]
3404
+ options[:options]['name'] = args[1]
3405
+ end
3406
+ if options[:payload]
3407
+ payload = options[:payload]
3408
+ # support -O OPTION switch on top of --payload
3409
+ if options[:options]
3410
+ payload ||= {}
3411
+ payload.deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) })
3412
+ end
3413
+ else
3414
+ options[:params] ||= {}
3415
+ options[:params].merge!({:serverGroupId => cluster['id']})
3416
+ option_types = add_affinity_group_option_types
3417
+ affinity_group = Morpheus::Cli::OptionTypes.prompt(option_types, options[:options], @api_client, options[:params])
3418
+
3419
+ # affinity_group_type = find_affinity_group_type_by_code(affinity_group['affinityGroupType'])
3420
+ # affinity_group['affinityGroupType'] = {id:affinity_group_type['id']}
3421
+
3422
+ # # affinity_group type options
3423
+ # unless affinity_group_type['optionTypes'].empty?
3424
+ # 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]))
3425
+ # end
3426
+
3427
+ # perms
3428
+ perms = prompt_permissions(options.merge({:for_affinity_group => true}), ['groupDefaults'])
3429
+
3430
+ affinity_group['resourcePermissions'] = perms['resourcePermissions'] unless perms['resourcePermissions'].nil?
3431
+ affinity_group['tenants'] = perms['tenantPermissions'] unless perms['tenantPermissions'].nil?
3432
+ affinity_group['visibility'] = perms['resourcePool']['visibility'] if !perms['resourcePool'].nil? && !perms['resourcePool']['visibility'].nil?
3433
+
3434
+ payload = {'affinityGroup' => affinity_group}
3435
+ end
3436
+
3437
+ @clusters_interface.setopts(options)
3438
+ if options[:dry_run]
3439
+ print_dry_run @clusters_interface.dry.create_affinity_group(cluster['id'], payload)
3440
+ return
3441
+ end
3442
+ json_response = @clusters_interface.create_affinity_group(cluster['id'], payload)
3443
+ if options[:json]
3444
+ puts as_json(json_response)
3445
+ else
3446
+ if json_response['success']
3447
+ if json_response['msg'] == nil
3448
+ print_green_success "Adding affinity group to cluster #{cluster['name']}"
3449
+ else
3450
+ print_green_success json_response['msg']
3451
+ end
3452
+ execution_id = json_response['executionId']
3453
+ if !options[:no_refresh] && execution_id
3454
+ wait_for_execution_request(json_response['executionId'], options.merge({waiting_status:['new', 'pending', 'executing']}))
3455
+ end
3456
+ else
3457
+ print_red_alert "Failed to create cluster affinity group #{json_response['msg']}"
3458
+ end
3459
+ end
3460
+ return 0
3461
+ rescue RestClient::Exception => e
3462
+ print_rest_exception(e, options)
3463
+ exit 1
3464
+ end
3465
+ end
3466
+
3467
+ def update_affinity_group(args)
3468
+ options = {}
3469
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
3470
+ opts.banner = subcommand_usage( "[cluster] [affinity group] [options]")
3471
+ opts.on('--active [on|off]', String, "Enable affinity group") do |val|
3472
+ options[:active] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == ''
3473
+ end
3474
+ # add_perms_options(opts, options, ['groupDefaults'])
3475
+ build_standard_update_options(opts, options)
3476
+ opts.footer = "Update a cluster affinity group.\n" +
3477
+ "[cluster] is required. This is the name or id of an existing cluster.\n" +
3478
+ "[affinity group] is required. This is the name or id of an existing affinity group."
3479
+ end
3480
+
3481
+ optparse.parse!(args)
3482
+ if args.count != 2
3483
+ raise_command_error "wrong number of arguments, expected 2 and got (#{args.count}) #{args}\n#{optparse}"
3484
+ end
3485
+ connect(options)
3486
+
3487
+ begin
3488
+ cluster = find_cluster_by_name_or_id(args[0])
3489
+ return 1 if cluster.nil?
3490
+ affinity_group = find_cluster_affinity_group_by_name_or_id(cluster['id'], args[1])
3491
+ if affinity_group.nil?
3492
+ print_red_alert "Affinity Group not found by '#{args[1]}'"
3493
+ exit 1
3494
+ end
3495
+ payload = nil
3496
+ if options[:payload]
3497
+ payload = options[:payload]
3498
+ # support -O OPTION switch on top of everything
3499
+ if options[:options]
3500
+ payload.deep_merge!({'affinityGroup' => options[:options].reject {|k,v| k.is_a?(Symbol) }})
3501
+ end
3502
+ else
3503
+ payload = {'affinityGroup' => {}}
3504
+ payload['affinityGroup']['active'] = options[:active].nil? ? (Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'active', 'fieldLabel' => 'Active', 'type' => 'checkbox', 'description' => 'Active', 'defaultValue' => true}], options[:options], @api_client))['active'] == 'on' : options[:active]
3505
+
3506
+ perms = prompt_permissions(options.merge({:available_plans => namespace_service_plans}), affinity_group['owner']['id'] == current_user['accountId'] ? ['plans', 'groupDefaults'] : ['plans', 'groupDefaults', 'visibility', 'tenants'])
3507
+ perms_payload = {}
3508
+ perms_payload['resourcePermissions'] = perms['resourcePermissions'] if !perms['resourcePermissions'].nil?
3509
+ perms_payload['tenantPermissions'] = perms['tenantPermissions'] if !perms['tenantPermissions'].nil?
3510
+
3511
+ payload['affinityGroup']['permissions'] = perms_payload
3512
+ payload['affinityGroup']['visibility'] = perms['resourcePool']['visibility'] if !perms['resourcePool'].nil? && !perms['resourcePool']['visibility'].nil?
3513
+
3514
+ # support -O OPTION switch on top of everything
3515
+ if options[:options]
3516
+ payload.deep_merge!({'affinityGroup' => options[:options].reject {|k,v| k.is_a?(Symbol) }})
3517
+ end
3518
+
3519
+ if payload['affinityGroup'].nil? || payload['affinityGroup'].empty?
3520
+ raise_command_error "Specify at least one option to update.\n#{optparse}"
3521
+ end
3522
+ end
3523
+
3524
+ @clusters_interface.setopts(options)
3525
+ if options[:dry_run]
3526
+ print_dry_run @clusters_interface.dry.update_affinity_group(cluster['id'], affinity_group['id'], payload)
3527
+ return
3528
+ end
3529
+ json_response = @clusters_interface.update_affinity_group(cluster['id'], affinity_group['id'], payload)
3530
+ if options[:json]
3531
+ puts as_json(json_response)
3532
+ elsif !options[:quiet]
3533
+ affinity_group = json_response['affinityGroup']
3534
+ print_green_success "Updated affinity group #{affinity_group['name']}"
3535
+ #get_args = [cluster["id"], affinity_group["id"]] + (options[:remote] ? ["-r",options[:remote]] : [])
3536
+ #get_namespace(get_args)
3537
+ end
3538
+ return 0
3539
+ rescue RestClient::Exception => e
3540
+ print_rest_exception(e, options)
3541
+ exit 1
3542
+ end
3543
+ end
3544
+
3545
+ def remove_affinity_group(args)
3546
+ default_refresh_interval = 5
3547
+ params = {}
3548
+ options = {}
3549
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
3550
+ opts.banner = subcommand_usage("[cluster] [affinity group]")
3551
+ # opts.on( '-f', '--force', "Force Delete" ) do
3552
+ # params[:force] = 'on'
3553
+ # end
3554
+ # opts.on('--refresh [SECONDS]', String, "Refresh until execution is complete. Default interval is #{default_refresh_interval} seconds.") do |val|
3555
+ # options[:refresh_interval] = val.to_s.empty? ? default_refresh_interval : val.to_f
3556
+ # end
3557
+ # opts.on(nil, '--no-refresh', "Do not refresh" ) do
3558
+ # options[:no_refresh] = true
3559
+ # end
3560
+ build_standard_remove_options(opts, options)
3561
+ opts.footer = "Delete an affinity group from a cluster.\n" +
3562
+ "[cluster] is required. This is the name or id of an existing cluster.\n" +
3563
+ "[affinity group] is required. This is the name or id of an existing affinity group."
3564
+ end
3565
+ optparse.parse!(args)
3566
+ verify_args!(args:args, optparse:optparse, count:2)
3567
+ connect(options)
3568
+ params.merge!(parse_query_options(options))
3569
+
3570
+ cluster = find_cluster_by_name_or_id(args[0])
3571
+ return 1 if cluster.nil?
3572
+
3573
+ affinity_group_id = args[1]
3574
+ if affinity_group_id.empty?
3575
+ raise_command_error "missing required worker parameter"
3576
+ end
3577
+
3578
+ affinity_group = find_cluster_affinity_group_by_name_or_id(cluster['id'], affinity_group_id)
3579
+ if affinity_group.nil?
3580
+ print_red_alert "Affinity Group not found for '#{affinity_group_id}'"
3581
+ return 1
3582
+ end
3583
+ unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to remove the cluster affinity group '#{affinity_group['name'] || affinity_group['id']}'?", options)
3584
+ return 9, "aborted command"
3585
+ end
3586
+
3587
+ @clusters_interface.setopts(options)
3588
+ if options[:dry_run]
3589
+ print_dry_run @clusters_interface.dry.destroy_affinity_group(cluster['id'], affinity_group['id'], params)
3590
+ return
3591
+ end
3592
+ json_response = @clusters_interface.destroy_affinity_group(cluster['id'], affinity_group['id'], params)
3593
+ if options[:json]
3594
+ puts as_json(json_response)
3595
+ else
3596
+ if json_response['success']
3597
+ print_green_success "Removed affinity group #{affinity_group['name']}"
3598
+ execution_id = json_response['executionId']
3599
+ if !options[:no_refresh] && execution_id
3600
+ wait_for_execution_request(execution_id, options.merge({waiting_status:['new', 'pending', 'executing']}))
3601
+ end
3602
+ else
3603
+ print_red_alert "Failed to remove cluster affinity_group #{json_response['msg']}"
3604
+ end
3605
+ end
3606
+ return 0, nil
3607
+ end
3608
+
3272
3609
  def api_config(args)
3273
3610
  options = {}
3274
3611
  optparse = Morpheus::Cli::OptionParser.new do |opts|
@@ -4866,4 +5203,27 @@ class Morpheus::Cli::Clusters
4866
5203
  connect(options)
4867
5204
  _list_container_groups(args, options, resource_type)
4868
5205
  end
5206
+
5207
+ def find_cluster_affinity_group_by_name_or_id(cluster_id, val)
5208
+ if val.to_s =~ /\A\d{1,}\Z/
5209
+ @clusters_interface.get_affinity_group(cluster_id, val)['affinityGroup'] rescue nil
5210
+ else
5211
+ @clusters_interface.list_affinity_groups(cluster_id, {name: val})['affinityGroups'][0]
5212
+ end
5213
+ end
5214
+
5215
+ def add_affinity_group_option_types
5216
+ [
5217
+ {'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true},
5218
+ {'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'},
5219
+ {'fieldName' => 'active', 'fieldLabel' => 'Active', 'type' => 'checkbox', 'defaultValue' => true},
5220
+ # {'fieldName' => 'pool.id', 'fieldLabel' => 'Cluster', 'type' => 'select', 'optionSourceType' => 'vmware', 'optionSource' => 'vmwareZonePoolClusters', 'description' => 'Select cluster for the affinity group.', 'required' => true},
5221
+ {'fieldName' => 'servers', 'fieldLabel' => 'Server', 'type' => 'multiSelect', 'optionSource' => 'searchServers', 'description' => 'Select servers to be in the affinity group.'},
5222
+ ]
5223
+ end
5224
+
5225
+ def format_affinity_type(affinity_type)
5226
+ affinity_type == "KEEP_SEPARATE" ? "Keep Separate" : "Keep Together"
5227
+ end
5228
+
4869
5229
  end