morpheus-cli 7.0.6 → 8.0.0

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.
@@ -13,18 +13,20 @@ class Morpheus::Cli::Clusters
13
13
  register_subcommands :upgrade_cluster
14
14
  register_subcommands :list_volumes, :remove_volume
15
15
  register_subcommands :list_namespaces, :get_namespace, :add_namespace, :update_namespace, :remove_namespace
16
- register_subcommands :list_containers, :remove_container, :restart_container
16
+ register_subcommands :list_containers, :remove_container, :restart_container, :get_container
17
17
  register_subcommands :list_deployments, :remove_deployment, :restart_deployment
18
18
  register_subcommands :list_stateful_sets, :remove_stateful_set, :restart_stateful_set
19
19
  register_subcommands :list_pods, :remove_pod, :restart_pod
20
20
  register_subcommands :list_jobs, :remove_job
21
21
  register_subcommands :list_services, :remove_service
22
- register_subcommands :list_datastores, :get_datastore, :update_datastore
22
+ register_subcommands :list_datastores, :get_datastore, :update_datastore, :add_datastore, :remove_datastore
23
23
  register_subcommands :update_permissions
24
24
  register_subcommands :api_config, :view_api_token, :view_kube_config
25
25
  register_subcommands :wiki, :update_wiki
26
26
  register_subcommands :apply_template
27
27
  register_subcommands :refresh
28
+ register_subcommands :list_replicasets, :list_daemonsets, :list_endpoints, :list_ingresses, :list_policies, :list_volumes, :list_volume_claims, :list_config_maps, :list_secrets
29
+ 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
28
30
 
29
31
  def connect(opts)
30
32
  @api_client = establish_remote_appliance_connection(opts)
@@ -32,6 +34,7 @@ class Morpheus::Cli::Clusters
32
34
  @groups_interface = @api_client.groups
33
35
  @cluster_layouts_interface = @api_client.library_cluster_layouts
34
36
  @security_groups_interface = @api_client.security_groups
37
+ @datastores_interface = @api_client.datastores
35
38
  #@security_group_rules_interface = @api_client.security_group_rules
36
39
  @cloud_resource_pools_interface = @api_client.cloud_resource_pools
37
40
  @resource_pool_groups_interface = @api_client.resource_pool_groups
@@ -220,6 +223,7 @@ class Morpheus::Cli::Clusters
220
223
  "Cloud" => lambda { |it| it['zone']['name'] },
221
224
  "Location" => lambda { |it| it['location'] },
222
225
  "Layout" => lambda { |it| it['layout'] ? it['layout']['name'] : ''},
226
+ "Integrations" => lambda {|it| format_name_and_id(it['integrations']) },
223
227
  "API Url" => 'serviceUrl',
224
228
  "Visibility" => lambda { |it| it['visibility'].to_s.capitalize },
225
229
  #"Groups" => lambda {|it| it['groups'].collect {|g| g.instance_of?(Hash) ? g['name'] : g.to_s }.join(', ') },
@@ -228,6 +232,8 @@ class Morpheus::Cli::Clusters
228
232
  "Created" => lambda {|it| format_local_dt(it['dateCreated']) },
229
233
  "Created By" => lambda {|it| it['createdBy'] ? it['createdBy']['username'] : '' },
230
234
  "Enabled" => lambda { |it| format_boolean(it['enabled']) },
235
+ "Managed" => lambda { |it| format_boolean(it['managed']) },
236
+ "Auto Power On VMs" => lambda { |it| format_boolean(it['autoRecoverPowerState']) },
231
237
  "Status" => lambda { |it| format_cluster_status(it) }
232
238
  }
233
239
  print_description_list(description_cols, cluster)
@@ -526,12 +532,7 @@ class Morpheus::Cli::Clusters
526
532
  resourceName = options[:resourceName]
527
533
 
528
534
  if !resourceName
529
- if options[:no_prompt]
530
- print_red_alert "No resource name provided"
531
- exit 1
532
- else
533
- resourceName = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'resourceName', 'type' => 'text', 'fieldLabel' => 'Resource Name', 'required' => true, 'description' => 'Resource Name.'}],options[:options],@api_client,{})['resourceName']
534
- end
535
+ resourceName = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'resourceName', 'type' => 'text', 'fieldLabel' => 'Resource Name', 'required' => false, 'description' => 'Resource Name.'}],options[:options],@api_client,{})['resourceName']
535
536
  end
536
537
 
537
538
  server_payload['name'] = resourceName
@@ -594,9 +595,17 @@ class Morpheus::Cli::Clusters
594
595
  # Provision Type
595
596
  provision_type = (layout && layout['provisionType'] ? layout['provisionType'] : nil) || get_provision_type_for_zone_type(cloud['zoneType']['id'])
596
597
  provision_type = @provision_types_interface.get(provision_type['id'])['provisionType'] if !provision_type.nil?
597
-
598
598
  api_params = {zoneId: cloud['id'], siteId: group['id'], layoutId: layout['id'], groupTypeId: cluster_type['id'], provisionType: provision_type['code'], provisionTypeId: provision_type['id']}
599
599
 
600
+ # Service Plan
601
+ service_plan = prompt_service_plan(api_params, options)
602
+ if service_plan
603
+ server_payload['plan'] = {'id' => service_plan['id'], 'code' => service_plan['code']}
604
+ api_params['planId'] = service_plan['id']
605
+ plan_opts = prompt_service_plan_options(service_plan, provision_type, options)
606
+ server_payload['servicePlanOptions'] = plan_opts if plan_opts && !plan_opts.empty?
607
+ end
608
+
600
609
  # Controller type
601
610
  server_types = @server_types_interface.list({computeTypeId: cluster_type['controllerTypes'].first['id'], zoneTypeId: cloud['zoneType']['id'], useZoneProvisionTypes: true})['serverTypes'].reject {|it| it['provisionType']['code'] == 'manual'}
602
611
  controller_provision_type = nil
@@ -613,7 +622,7 @@ class Morpheus::Cli::Clusters
613
622
  end
614
623
  end
615
624
 
616
- if controller_provision_type && resource_pool = prompt_resource_pool(group, cloud, nil, controller_provision_type, options)
625
+ if controller_provision_type && resource_pool = prompt_resource_pool(group, cloud, service_plan, controller_provision_type, options)
617
626
  server_payload['config']['resourcePoolId'] = resource_pool['id']
618
627
  api_params['config'] ||= {}
619
628
  api_params['config']['resourcePool'] = resource_pool['id']
@@ -622,16 +631,6 @@ class Morpheus::Cli::Clusters
622
631
  end
623
632
  end
624
633
 
625
- # Service Plan
626
- service_plan = prompt_service_plan(api_params, options)
627
-
628
- if service_plan
629
- server_payload['plan'] = {'id' => service_plan['id'], 'code' => service_plan['code']}
630
- api_params['planId'] = service_plan['id']
631
- plan_opts = prompt_service_plan_options(service_plan, provision_type, options)
632
- server_payload['servicePlanOptions'] = plan_opts if plan_opts && !plan_opts.empty?
633
- end
634
-
635
634
  # Multi-disk / prompt for volumes
636
635
  if provision_type['hasVolumes']
637
636
  volumes = options[:volumes] || prompt_volumes(service_plan, provision_type, options, @api_client, api_params)
@@ -653,6 +652,8 @@ class Morpheus::Cli::Clusters
653
652
  metadata_option_type = option_type_list.find {|type| type['fieldName'] == 'metadata' }
654
653
  option_type_list = option_type_list.reject {|type| type['fieldName'] == 'metadata' }
655
654
 
655
+ server_count = layout['serverCount']
656
+
656
657
  # KLUDGE: google zone required for network selection
657
658
  if option_type = option_type_list.find {|type| type['code'] == 'computeServerType.googleLinux.googleZoneId'}
658
659
  server_payload.deep_merge!(Morpheus::Cli::OptionTypes.prompt([option_type], options[:options], @api_client, api_params))
@@ -684,6 +685,10 @@ class Morpheus::Cli::Clusters
684
685
  # Layout template options
685
686
  cluster_payload.deep_merge!(Morpheus::Cli::OptionTypes.prompt(load_layout_options(cluster_payload), options[:options], @api_client, api_params, options[:no_prompt], true))
686
687
 
688
+ # Set node count for ssh hosts
689
+ ssh_host_option = option_type_list.select{|it| it['fieldName'] == 'sshHosts'}.first
690
+ ssh_host_option['minCount'] = server_count unless ssh_host_option.nil?
691
+
687
692
  # Server options
688
693
  server_payload.deep_merge!(Morpheus::Cli::OptionTypes.prompt(option_type_list, options[:options].deep_merge({:context_map => {'domain' => ''}}), @api_client, api_params, options[:no_prompt], true))
689
694
 
@@ -729,7 +734,7 @@ class Morpheus::Cli::Clusters
729
734
 
730
735
  # Host / Domain
731
736
  server_payload['networkDomain'] = options[:domain] || Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'networkDomain', 'fieldLabel' => 'Network Domain', 'type' => 'select', 'required' => false, 'optionSource' => 'networkDomains'}], options[:options], @api_client, api_params)['networkDomain']
732
- server_payload['hostname'] = options[:hostname] || Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'hostname', 'fieldLabel' => 'Hostname', 'type' => 'text', 'required' => true, 'description' => 'Hostname', 'defaultValue' => resourceName}], options[:options], @api_client, api_params)['hostname']
737
+ server_payload['hostname'] = options[:hostname] || Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'hostname', 'fieldLabel' => 'Hostname', 'type' => 'text', 'required' => false, 'description' => 'Hostname', 'defaultValue' => resourceName}], options[:options], @api_client, api_params)['hostname']
733
738
 
734
739
  # Kube Default Repo
735
740
  if cluster_payload['type'] == 'kubernetes-cluster'
@@ -797,12 +802,18 @@ class Morpheus::Cli::Clusters
797
802
  opts.on('--managed [on|off]', String, "Can be used to enable / disable managed cluster. Default is on") do |val|
798
803
  options[:managed] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
799
804
  end
805
+ opts.on('--autoRecoverPowerState [on|off]', String, "Automatically Power On VMs") do |val|
806
+ options[:autoRecoverPowerState] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
807
+ end
800
808
  opts.on( nil, '--refresh', "Refresh cluster" ) do
801
809
  options[:refresh] = true
802
810
  end
803
811
  opts.on("--tenant ACCOUNT", String, "Account ID or Name" ) do |val|
804
812
  options[:tenant] = val
805
813
  end
814
+ opts.on('--integrations [LIST]', Array, "Updates Cluster Integration(s), comma separated list of integration IDs") do |list|
815
+ options[:integrations] = list ? list.collect {|it| it.to_s.strip.to_i } : []
816
+ end
806
817
  build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote])
807
818
  opts.footer = "Update a cluster.\n" +
808
819
  "[cluster] is required. This is the name or id of an existing cluster."
@@ -832,15 +843,18 @@ class Morpheus::Cli::Clusters
832
843
  else
833
844
  cluster = find_cluster_by_name_or_id(args[0])
834
845
  cluster_payload = {}
846
+ cluster_payload.deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) })
835
847
  cluster_payload['name'] = options[:name] if !options[:name].empty?
836
848
  cluster_payload['description'] = options[:description] if !options[:description].empty?
837
849
  cluster_payload['labels'] = options[:labels] if !options[:labels].nil?
838
850
  cluster_payload['enabled'] = options[:active] if !options[:active].nil?
839
851
  cluster_payload['managed'] = options[:managed] if !options[:managed].nil?
852
+ cluster_payload['autoRecoverPowerState'] = options[:autoRecoverPowerState] if !options[:autoRecoverPowerState].nil?
840
853
  cluster_payload['serviceUrl'] = options[:apiUrl] if !options[:apiUrl].nil?
841
854
  cluster_payload['serviceToken'] = options[:apiToken] if !options[:apiToken].nil?
842
855
  cluster_payload['refresh'] = options[:refresh] if options[:refresh] == true
843
856
  cluster_payload['tenant'] = options[:tenant] if !options[:tenant].nil?
857
+ cluster_payload['integrations'] = options[:integrations] if !options[:integrations].nil?
844
858
  payload = {"cluster" => cluster_payload}
845
859
  end
846
860
 
@@ -848,10 +862,9 @@ class Morpheus::Cli::Clusters
848
862
  print_red_alert "No clusters available for update"
849
863
  exit 1
850
864
  end
851
-
852
- if cluster_payload.empty?
853
- print_green_success "Nothing to update"
854
- exit 1
865
+
866
+ if payload['cluster'].empty?
867
+ raise_command_error "Specify at least one option to update.\n#{optparse}"
855
868
  end
856
869
 
857
870
  @clusters_interface.setopts(options)
@@ -2206,6 +2219,7 @@ class Morpheus::Cli::Clusters
2206
2219
  :id, :status, :name, :cpu, :memory, :storage
2207
2220
  ]
2208
2221
  print as_pretty_table(rows, columns, options)
2222
+ print_results_pagination(json_response)
2209
2223
  end
2210
2224
  print reset,"\n"
2211
2225
  return 0
@@ -2215,6 +2229,43 @@ class Morpheus::Cli::Clusters
2215
2229
  end
2216
2230
  end
2217
2231
 
2232
+ def _get_container_group(args, options, resource_type)
2233
+ begin
2234
+ cluster = find_cluster_by_name_or_id(args[0])
2235
+ id = args[1]
2236
+ return 1 if cluster.nil?
2237
+
2238
+ params = {}
2239
+ params.merge!(parse_list_options(options))
2240
+ params['resourceLevel'] = options[:resourceLevel] if !options[:resourceLevel].nil?
2241
+ @clusters_interface.setopts(options)
2242
+ if options[:dry_run]
2243
+ print_dry_run @clusters_interface.dry.get_container_group(cluster['id'], resource_type, id, params)
2244
+ return
2245
+ end
2246
+ container_group = @clusters_interface.get_container_group(cluster['id'], resource_type, id, params)['resource']
2247
+
2248
+ render_result = render_with_format(container_group, options, 'containers')
2249
+ return 0 if render_result
2250
+ resource_is = options['title'] ? options['title'].capitalize : resource_type.capitalize
2251
+ title = "Morpheus Cluster #{cluster['name']}: #{resource_is}"
2252
+ print_h1 title
2253
+ print cyan
2254
+ description_cols = {
2255
+ "ID" => 'id',
2256
+ "Name" => 'name',
2257
+ "Status" => 'status',
2258
+ "metadata" => lambda { |it| as_json(it['metadata']) }
2259
+ }
2260
+ print_description_list(description_cols, container_group)
2261
+ print reset,"\n"
2262
+ return 0
2263
+ rescue RestClient::Exception => e
2264
+ print_rest_exception(e, options)
2265
+ exit 1
2266
+ end
2267
+ end
2268
+
2218
2269
  def _remove_container_group(args, options, resource_type)
2219
2270
  begin
2220
2271
  cluster = find_cluster_by_name_or_id(args[0])
@@ -2322,6 +2373,26 @@ class Morpheus::Cli::Clusters
2322
2373
  _list_container_groups(args, options,resource_type)
2323
2374
  end
2324
2375
 
2376
+ def get_deployment(args)
2377
+ resource_type = 'deployment'
2378
+ options = {}
2379
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
2380
+ opts.banner = subcommand_usage( "[cluster]")
2381
+ opts.on("--resource-level LEVEL", String, "Resource Level") do |val|
2382
+ options[:resourceLevel] = val.to_s
2383
+ end
2384
+ build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
2385
+ opts.footer = "get #{resource_type} for a cluster.\n" +
2386
+ "[cluster] and [#{resource_type}] is required. This is the name or id of an existing cluster, and the id of the #{resource_type}"
2387
+ end
2388
+ optparse.parse!(args)
2389
+ if args.count != 2
2390
+ raise_command_error "wrong number of arguments, expected 2 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
2391
+ end
2392
+ connect(options)
2393
+ _get_container_group(args, options, resource_type)
2394
+ end
2395
+
2325
2396
  def remove_deployment(args)
2326
2397
  resource_type = 'deployment'
2327
2398
  options = {}
@@ -2440,6 +2511,26 @@ class Morpheus::Cli::Clusters
2440
2511
  _list_container_groups(args, options, resource_type)
2441
2512
  end
2442
2513
 
2514
+ def get_pod(args)
2515
+ resource_type = 'pod'
2516
+ options = {}
2517
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
2518
+ opts.banner = subcommand_usage( "[cluster]")
2519
+ opts.on("--resource-level LEVEL", String, "Resource Level") do |val|
2520
+ options[:resourceLevel] = val.to_s
2521
+ end
2522
+ build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
2523
+ opts.footer = "get #{resource_type} for a cluster.\n" +
2524
+ "[cluster] and [#{resource_type}] is required. This is the name or id of an existing cluster, and the id of the #{resource_type}"
2525
+ end
2526
+ optparse.parse!(args)
2527
+ if args.count != 2
2528
+ raise_command_error "wrong number of arguments, expected 2 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
2529
+ end
2530
+ connect(options)
2531
+ _get_container_group(args, options, resource_type)
2532
+ end
2533
+
2443
2534
  def remove_pod(args)
2444
2535
  resource_type = 'pod'
2445
2536
  options = {}
@@ -2909,6 +3000,130 @@ class Morpheus::Cli::Clusters
2909
3000
  end
2910
3001
  end
2911
3002
 
3003
+ def add_datastore(args)
3004
+ options = {}
3005
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
3006
+ opts.banner = subcommand_usage( "[cluster] [options]")
3007
+ build_option_type_options(opts, options, add_datastore_option_types)
3008
+ build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote])
3009
+ opts.footer = "Add datastore to a cluster.\n" +
3010
+ "[cluster] is required. This is the name or id of an existing cluster.\n" +
3011
+ "[name] is required. This is the name of the new datastore."
3012
+ end
3013
+
3014
+ optparse.parse!(args)
3015
+ if args.count != 1 and args.count != 2
3016
+ raise_command_error "wrong number of arguments, expected 1-2 and got (#{args.count}) #{args}\n#{optparse}"
3017
+ end
3018
+ connect(options)
3019
+
3020
+ begin
3021
+ cluster = find_cluster_by_name_or_id(args[0])
3022
+ return 1 if cluster.nil?
3023
+ if options[:payload]
3024
+ payload = options[:payload]
3025
+ # support -O OPTION switch on top of --payload
3026
+ if options[:options]
3027
+ payload ||= {}
3028
+ payload.deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) })
3029
+ end
3030
+ else
3031
+ options[:params] ||= {}
3032
+ options[:params].merge!({:serverGroupId => cluster['id']})
3033
+
3034
+ datastore = Morpheus::Cli::OptionTypes.prompt(add_datastore_option_types, options[:options], @api_client, options[:params])
3035
+
3036
+ datastore_type = find_datastore_type_by_code(datastore['datastoreType'])
3037
+ datastore['datastoreType'] = {id:datastore_type['id']}
3038
+
3039
+ # datastore type options
3040
+ unless datastore_type['optionTypes'].empty?
3041
+ datastore.merge!(Morpheus::Cli::OptionTypes.prompt(datastore_type['optionTypes'], options[:options].deep_merge({:context_map => {'domain' => ''}, :checkbox_as_boolean => true}), @api_client, options[:params]))
3042
+ end
3043
+
3044
+ # perms
3045
+ perms = prompt_permissions(options.merge({:for_datastore => true}), ['plans', 'groupDefaults'])
3046
+
3047
+ datastore['resourcePermissions'] = perms['resourcePermissions'] unless perms['resourcePermissions'].nil?
3048
+ datastore['tenants'] = perms['tenantPermissions'] unless perms['tenantPermissions'].nil?
3049
+ datastore['visibility'] = perms['resourcePool']['visibility'] if !perms['resourcePool'].nil? && !perms['resourcePool']['visibility'].nil?
3050
+
3051
+ payload = {datastore:datastore}
3052
+ end
3053
+
3054
+ @clusters_interface.setopts(options)
3055
+ if options[:dry_run]
3056
+ print_dry_run @clusters_interface.dry.create_datastore(cluster['id'], payload)
3057
+ return
3058
+ end
3059
+ json_response = @clusters_interface.create_datastore(cluster['id'], payload)
3060
+ if options[:json]
3061
+ puts as_json(json_response)
3062
+ elsif json_response['success']
3063
+ if json_response['msg'] == nil
3064
+ print_green_success "Added datastore to cluster #{cluster['name']}"
3065
+ else
3066
+ print_green_success json_response['msg']
3067
+ end
3068
+ end
3069
+ return 0
3070
+ rescue RestClient::Exception => e
3071
+ print_rest_exception(e, options)
3072
+ exit 1
3073
+ end
3074
+ end
3075
+
3076
+ def remove_datastore(args)
3077
+ params = {}
3078
+ options = {}
3079
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
3080
+ opts.banner = subcommand_usage("[cluster] [datastore]")
3081
+ opts.on( '-f', '--force', "Force Delete" ) do
3082
+ params[:force] = 'on'
3083
+ end
3084
+ build_standard_remove_options(opts, options)
3085
+ opts.footer = "Delete a datastore from a cluster.\n" +
3086
+ "[cluster] is required. This is the name or id of an existing cluster.\n" +
3087
+ "[datastore] is required. This is the name or id of an existing datastore."
3088
+ end
3089
+ optparse.parse!(args)
3090
+ verify_args!(args:args, optparse:optparse, count:2)
3091
+ connect(options)
3092
+ params.merge!(parse_query_options(options))
3093
+
3094
+ cluster = find_cluster_by_name_or_id(args[0])
3095
+ return 1 if cluster.nil?
3096
+
3097
+ datastore_id = args[1]
3098
+ if datastore_id.empty?
3099
+ raise_command_error "missing required worker parameter"
3100
+ end
3101
+
3102
+ datastore = find_datastore_by_name_or_id(cluster['id'], datastore_id)
3103
+ if datastore.nil?
3104
+ print_red_alert "Datastore not found for '#{datastore_id}'"
3105
+ return 1
3106
+ end
3107
+ unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to remove the cluster datastore '#{datastore['name'] || datastore['id']}'?", options)
3108
+ return 9, "aborted command"
3109
+ end
3110
+
3111
+ @clusters_interface.setopts(options)
3112
+ if options[:dry_run]
3113
+ print_dry_run @clusters_interface.dry.destroy_datastore(cluster['id'], datastore['id'], params)
3114
+ return
3115
+ end
3116
+ json_response = @clusters_interface.destroy_datastore(cluster['id'], datastore['id'], params)
3117
+ render_response(json_response, options) do
3118
+ msg = "Datastore #{datastore['name']} is being removed from cluster #{cluster['name']}..."
3119
+ if json_response['msg']
3120
+ msg = json_response['msg']
3121
+ end
3122
+ print_green_success msg
3123
+ end
3124
+ return 0, nil
3125
+ end
3126
+
2912
3127
  def update_datastore(args)
2913
3128
  options = {}
2914
3129
  optparse = Morpheus::Cli::OptionParser.new do |opts|
@@ -3971,6 +4186,25 @@ class Morpheus::Cli::Clusters
3971
4186
  json_results['datastores'].empty? ? nil : json_results['datastores'][0]
3972
4187
  end
3973
4188
 
4189
+ def find_datastore_type_by_code_or_id(val)
4190
+ (val.to_s =~ /\A\d{1,}\Z/) ? find_datastore_type_by_id(val) : find_datastore_type_by_code(val)
4191
+ end
4192
+
4193
+ def find_datastore_type_by_id(id)
4194
+ get_datastore_types.find { |it| it['id'] == id }
4195
+ end
4196
+
4197
+ def find_datastore_type_by_code(code)
4198
+ get_datastore_types.find { |it| it['code'].downcase == code.downcase }
4199
+ end
4200
+
4201
+ def get_datastore_types(refresh=false)
4202
+ if !@datastore_types || refresh
4203
+ @datastore_types = @datastores_interface.types()['datastoreTypes']
4204
+ end
4205
+ @datastore_types
4206
+ end
4207
+
3974
4208
  def find_job_by_name_or_id(cluster_id, val)
3975
4209
  if val.to_s =~ /\A\d{1,}\Z/
3976
4210
  params = {jobId: val.to_i}
@@ -4276,6 +4510,14 @@ class Morpheus::Cli::Clusters
4276
4510
  end
4277
4511
  end
4278
4512
 
4513
+ def add_datastore_option_types
4514
+ [
4515
+ {'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'displayOrder' => 1},
4516
+ {'fieldName' => 'datastoreType', 'fieldLabel' => 'Type', 'type' => 'select', 'optionSource' => 'datastoreTypes', 'description' => 'Choose a datastore type.', 'required' => true, 'displayOrder' => 2},
4517
+ {'fieldName' => 'active', 'fieldLabel' => 'Active', 'type' => 'checkbox', 'required' => true, 'displayOrder' => 3, 'defaultValue' => true},
4518
+ ]
4519
+ end
4520
+
4279
4521
  def prompt_resource_pool(group, cloud, service_plan, provision_type, options)
4280
4522
  resource_pool = nil
4281
4523
 
@@ -4452,4 +4694,135 @@ class Morpheus::Cli::Clusters
4452
4694
  exit 1
4453
4695
  end
4454
4696
  end
4697
+
4698
+ def list_replicasets(args)
4699
+ _list_resources(args, 'replicaset')
4700
+ end
4701
+
4702
+ def get_replicaset(args)
4703
+ _get_resource(args, 'replicaset')
4704
+ end
4705
+
4706
+ def list_daemonsets(args)
4707
+ _list_resources(args, 'daemonset')
4708
+ end
4709
+
4710
+ def get_daemonset(args)
4711
+ _get_resource(args, 'daemonset')
4712
+ end
4713
+
4714
+
4715
+ def list_endpoints(args)
4716
+ _list_resources(args, 'endpoint')
4717
+ end
4718
+
4719
+ def get_endpoint(args)
4720
+ _get_resource(args, 'endpoint')
4721
+ end
4722
+
4723
+ def list_ingresses(args)
4724
+ _list_resources(args, 'ingresse')
4725
+ end
4726
+
4727
+ def get_ingress(args)
4728
+ _get_resource(args, 'ingresse', {'title' => 'ingress'})
4729
+ end
4730
+
4731
+ def list_policies(args)
4732
+ _list_resources(args, 'policie')
4733
+ end
4734
+
4735
+ def get_policy(args)
4736
+ _get_resource(args, 'policie', {'title' => 'policy'})
4737
+ end
4738
+
4739
+ def list_volume_claims(args)
4740
+ _list_resources(args, 'volumeclaim')
4741
+ end
4742
+
4743
+ def get_volume_claim(args)
4744
+ _get_resource(args, 'volumeclaim')
4745
+ end
4746
+
4747
+ def list_volumes(args)
4748
+ _list_resources(args, 'volume')
4749
+ end
4750
+
4751
+ def get_volume(args)
4752
+ _get_resource(args, 'volume')
4753
+ end
4754
+
4755
+
4756
+ def list_config_maps(args)
4757
+ _list_resources(args, 'configmap')
4758
+ end
4759
+
4760
+ def get_config_map(args)
4761
+ _get_resource(args, 'configmap')
4762
+ end
4763
+
4764
+ def list_secrets(args)
4765
+ _list_resources(args, 'secret')
4766
+ end
4767
+
4768
+ def get_secret(args)
4769
+ _get_resource(args, 'secret')
4770
+ end
4771
+
4772
+ def get_container(args)
4773
+ _get_resource(args, 'container')
4774
+ end
4775
+
4776
+ def get_stateful_set(args)
4777
+ _get_resource(args, 'statefulset')
4778
+
4779
+ end
4780
+
4781
+ def get_job(args)
4782
+ _get_resource(args, 'job')
4783
+ end
4784
+
4785
+ def get_service(args)
4786
+ _get_resource(args, 'service')
4787
+ end
4788
+
4789
+ private
4790
+
4791
+ def _get_resource(args, resource_type, options = {})
4792
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
4793
+ opts.banner = subcommand_usage("[cluster] [id]")
4794
+ opts.on("--resource-level LEVEL", String, "Resource Level") do |val|
4795
+ options[:resourceLevel] = val.to_s
4796
+ end
4797
+ build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
4798
+ opts.footer = "get #{resource_type} for a cluster.\n" +
4799
+ "[cluster] and [#{resource_type}] is required. This is the name or id of an existing cluster, and the id of the #{resource_type}"
4800
+ end
4801
+ optparse.parse!(args)
4802
+
4803
+ if args.count != 2
4804
+ raise_command_error "wrong number of arguments, expected 2 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
4805
+ end
4806
+
4807
+ connect(options)
4808
+ _get_container_group(args, options, resource_type)
4809
+ end
4810
+
4811
+ def _list_resources(args, resource_type, options = {})
4812
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
4813
+ opts.banner = subcommand_usage( "[cluster]")
4814
+ opts.on("--resource-level LEVEL", String, "Resource Level") do |val|
4815
+ options[:resourceLevel] = val.to_s
4816
+ end
4817
+ build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
4818
+ opts.footer = "List #{resource_type}s for a cluster.\n" +
4819
+ "[cluster] is required. This is the name or id of an existing cluster."
4820
+ end
4821
+ optparse.parse!(args)
4822
+ if args.count != 1
4823
+ raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
4824
+ end
4825
+ connect(options)
4826
+ _list_container_groups(args, options, resource_type)
4827
+ end
4455
4828
  end
@@ -1009,6 +1009,9 @@ class Morpheus::Cli::Hosts
1009
1009
  opts.on('--ssh-password VALUE', String, "SSH Password") do |val|
1010
1010
  params['sshPassword'] = val == "null" ? nil : val
1011
1011
  end
1012
+ opts.on('--ssh-key-pair ID', String, "SSH Key Pair ID") do |val|
1013
+ params['sshKeyPair'] = val == "null" ? nil : {"id" => val.to_i}
1014
+ end
1012
1015
  opts.on('--power-schedule-type ID', String, "Power Schedule Type ID") do |val|
1013
1016
  params['powerScheduleType'] = val == "null" ? nil : val
1014
1017
  end
@@ -2746,6 +2746,9 @@ class Morpheus::Cli::Instances
2746
2746
  options = {}
2747
2747
  optparse = Morpheus::Cli::OptionParser.new do |opts|
2748
2748
  opts.banner = subcommand_usage("[instance]")
2749
+ opts.on('--include-network-interfaces','--include-network-interfaces', "Populate payload networkInterfaces with current interfaces") do
2750
+ options[:include_nics] = true
2751
+ end
2749
2752
  build_standard_update_options(opts, options)
2750
2753
  end
2751
2754
  optparse.parse!(args)
@@ -2775,11 +2778,17 @@ class Morpheus::Cli::Instances
2775
2778
  plan_id = instance['plan']['id']
2776
2779
  resource_pool_id = instance['config']['resourcePoolId'] if instance['config']
2777
2780
  current_plan_name = instance['plan']['name']
2778
- current_interfaces = get_instance_interfaces(instance)
2779
- if current_interfaces != false
2780
- payload['networkInterfaces'] = current_interfaces
2781
- end
2782
2781
 
2782
+ # JD: networkInterfaces should not be needed but pre 7.0.8/8.0.0 the API does expect it to be passed
2783
+ # However if the instance has more than one server this creates duplicate nics and breaks things
2784
+ # so only continue to do it if the instance has just one server and remote version is pre 7.0.8
2785
+ # should also only do this if remote version < 7.0.8
2786
+ if options[:include_nics] || (!remote_version_gte("7.0.8") && instance['servers'] && instance['servers'].size == 1)
2787
+ current_interfaces = get_instance_interfaces(instance)
2788
+ if current_interfaces != false
2789
+ payload['networkInterfaces'] = current_interfaces
2790
+ end
2791
+ end
2783
2792
 
2784
2793
  # need to GET provision type for some settings...
2785
2794
  provision_type = @provision_types_interface.get(instance['layout']['provisionTypeId'])['provisionType']
@@ -5576,6 +5585,8 @@ private
5576
5585
  details['interfaces'].each do |inter|
5577
5586
  interfaces.push(inter)
5578
5587
  end
5588
+ # only include the first one or it will create duplicates
5589
+ break
5579
5590
  end
5580
5591
  return interfaces
5581
5592
  rescue
@@ -5583,4 +5594,22 @@ private
5583
5594
  end
5584
5595
  end
5585
5596
 
5597
+ def remote_version_gte(required_version)
5598
+ version = @remote_appliance[:build_version]
5599
+ return false if version.nil?
5600
+ version_numbers = version.split(".")
5601
+ required_version_numbers = required_version.split(".")
5602
+ result = true
5603
+ required_version_numbers.each_with_index do |v, i|
5604
+ if version_numbers[i].to_i > v.to_i
5605
+ break
5606
+ elsif version_numbers[i].to_i < v.to_i
5607
+ result = false
5608
+ break
5609
+ else
5610
+ # keep going
5611
+ end
5612
+ end
5613
+ return result
5614
+ end
5586
5615
  end