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.
- checksums.yaml +4 -4
- data/Dockerfile +1 -1
- data/lib/morpheus/api/api_client.rb +11 -0
- data/lib/morpheus/api/clusters_interface.rb +25 -0
- data/lib/morpheus/api/datastores_interface.rb +6 -0
- data/lib/morpheus/api/library_operating_systems_interface.rb +63 -0
- data/lib/morpheus/api/processes_interface.rb +17 -5
- data/lib/morpheus/cli/commands/backup_jobs_command.rb +50 -17
- data/lib/morpheus/cli/commands/backups_command.rb +36 -9
- data/lib/morpheus/cli/commands/clusters.rb +398 -25
- data/lib/morpheus/cli/commands/hosts.rb +3 -0
- data/lib/morpheus/cli/commands/instances.rb +33 -4
- data/lib/morpheus/cli/commands/library_container_types_command.rb +6 -0
- data/lib/morpheus/cli/commands/library_operating_systems_command.rb +671 -0
- data/lib/morpheus/cli/commands/license.rb +11 -1
- data/lib/morpheus/cli/commands/plugins.rb +2 -2
- data/lib/morpheus/cli/commands/processes_command.rb +71 -1
- data/lib/morpheus/cli/commands/setup.rb +32 -18
- data/lib/morpheus/cli/commands/virtual_images.rb +10 -2
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +28 -2
- data/lib/morpheus/cli/option_types.rb +9 -2
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/formatters.rb +12 -0
- metadata +5 -3
@@ -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
|
-
|
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,
|
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' =>
|
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
|
853
|
-
|
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
|