morpheus-cli 4.0.0.1 → 4.1.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/lib/morpheus/api.rb +10 -0
- data/lib/morpheus/api/api_client.rb +24 -3
- data/lib/morpheus/api/clouds_interface.rb +15 -0
- data/lib/morpheus/api/clusters_interface.rb +276 -0
- data/lib/morpheus/api/library_compute_type_layouts_interface.rb +26 -0
- data/lib/morpheus/api/logs_interface.rb +6 -0
- data/lib/morpheus/api/network_subnet_types_interface.rb +26 -0
- data/lib/morpheus/api/network_subnets_interface.rb +47 -0
- data/lib/morpheus/api/provision_types_interface.rb +6 -0
- data/lib/morpheus/api/security_groups_interface.rb +12 -3
- data/lib/morpheus/api/servers_interface.rb +15 -0
- data/lib/morpheus/api/service_plans_interface.rb +30 -0
- data/lib/morpheus/api/subnets_interface.rb +47 -0
- data/lib/morpheus/cli.rb +1 -0
- data/lib/morpheus/cli/apps.rb +20 -18
- data/lib/morpheus/cli/cli_command.rb +5 -1
- data/lib/morpheus/cli/clusters.rb +3952 -0
- data/lib/morpheus/cli/containers_command.rb +70 -2
- data/lib/morpheus/cli/hosts.rb +69 -53
- data/lib/morpheus/cli/instances.rb +33 -33
- data/lib/morpheus/cli/library_container_types_command.rb +2 -1
- data/lib/morpheus/cli/library_option_lists_command.rb +13 -8
- data/lib/morpheus/cli/mixins/accounts_helper.rb +43 -0
- data/lib/morpheus/cli/mixins/print_helper.rb +10 -2
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +53 -3
- data/lib/morpheus/cli/networks_command.rb +883 -36
- data/lib/morpheus/cli/option_types.rb +37 -14
- data/lib/morpheus/cli/roles.rb +78 -77
- data/lib/morpheus/cli/user_settings_command.rb +34 -5
- data/lib/morpheus/cli/version.rb +1 -1
- metadata +10 -2
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'morpheus/api/api_client'
|
2
|
+
|
3
|
+
class Morpheus::NetworkSubnetsInterface < Morpheus::APIClient
|
4
|
+
def initialize(access_token, refresh_token,expires_at = nil, base_url=nil)
|
5
|
+
@access_token = access_token
|
6
|
+
@refresh_token = refresh_token
|
7
|
+
@base_url = base_url
|
8
|
+
@expires_at = expires_at
|
9
|
+
end
|
10
|
+
|
11
|
+
def get(network_id, id, params={})
|
12
|
+
raise "#{self.class}.get() passed a blank id!" if id.to_s == ''
|
13
|
+
url = "#{@base_url}/api/networks/#{network_id}/subnets/#{id}"
|
14
|
+
headers = { params: params, authorization: "Bearer #{@access_token}" }
|
15
|
+
opts = {method: :get, url: url, headers: headers}
|
16
|
+
execute(opts)
|
17
|
+
end
|
18
|
+
|
19
|
+
def list(network_id, params={})
|
20
|
+
url = "#{@base_url}/api/networks/#{network_id}/subnets"
|
21
|
+
headers = { params: params, authorization: "Bearer #{@access_token}" }
|
22
|
+
opts = {method: :get, url: url, headers: headers}
|
23
|
+
execute(opts)
|
24
|
+
end
|
25
|
+
|
26
|
+
def create(network_id, payload)
|
27
|
+
url = "#{@base_url}/api/networks/#{network_id}/subnets"
|
28
|
+
headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
29
|
+
opts = {method: :post, url: url, headers: headers, payload: payload.to_json}
|
30
|
+
execute(opts)
|
31
|
+
end
|
32
|
+
|
33
|
+
def update(network_id, id, payload)
|
34
|
+
url = "#{@base_url}/api/networks/#{network_id}/subnets/#{id}"
|
35
|
+
headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
36
|
+
opts = {method: :put, url: url, headers: headers, payload: payload.to_json}
|
37
|
+
execute(opts)
|
38
|
+
end
|
39
|
+
|
40
|
+
def destroy(network_id, id, params={})
|
41
|
+
url = "#{@base_url}/api/networks/#{network_id}/subnets/#{id}"
|
42
|
+
headers = { :params => params, :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
43
|
+
opts = {method: :delete, url: url, headers: headers}
|
44
|
+
execute(opts)
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -8,6 +8,12 @@ class Morpheus::ProvisionTypesInterface < Morpheus::APIClient
|
|
8
8
|
@expires_at = expires_at
|
9
9
|
end
|
10
10
|
|
11
|
+
def list(params={})
|
12
|
+
url = "#{@base_url}/api/provision-types"
|
13
|
+
headers = { params: params, authorization: "Bearer #{@access_token}" }
|
14
|
+
execute(method: :get, url: url, headers: headers)
|
15
|
+
end
|
16
|
+
|
11
17
|
def get(options=nil)
|
12
18
|
url = "#{@base_url}/api/provision-types"
|
13
19
|
headers = { params: {}, authorization: "Bearer #{@access_token}" }
|
@@ -15,9 +15,18 @@ class Morpheus::SecurityGroupsInterface < Morpheus::APIClient
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def get(id, params={})
|
18
|
-
url = "#{@base_url}/api/security-groups
|
19
|
-
headers = { params:
|
20
|
-
|
18
|
+
url = "#{@base_url}/api/security-groups"
|
19
|
+
headers = { params: {}, authorization: "Bearer #{@access_token}" }
|
20
|
+
|
21
|
+
if options.is_a?(Hash)
|
22
|
+
headers[:params].merge!(options)
|
23
|
+
elsif options.is_a?(Numeric)
|
24
|
+
url = "#{url}/#{options}"
|
25
|
+
elsif options.is_a?(String)
|
26
|
+
headers[:params]['name'] = options
|
27
|
+
end
|
28
|
+
opts = {method: :get, url: url, headers: headers}
|
29
|
+
execute(opts)
|
21
30
|
end
|
22
31
|
|
23
32
|
def create(payload)
|
@@ -122,6 +122,21 @@ class Morpheus::ServersInterface < Morpheus::APIClient
|
|
122
122
|
execute(opts)
|
123
123
|
end
|
124
124
|
|
125
|
+
def service_plan(options)
|
126
|
+
url = "#{@base_url}/api/service-plans"
|
127
|
+
headers = { params: {}, authorization: "Bearer #{@access_token}" }
|
128
|
+
|
129
|
+
if options.is_a?(Hash)
|
130
|
+
headers[:params].merge!(options)
|
131
|
+
elsif options.is_a?(Numeric)
|
132
|
+
url = "#{url}/#{options}"
|
133
|
+
elsif options.is_a?(String)
|
134
|
+
headers[:params]['name'] = options
|
135
|
+
end
|
136
|
+
opts = {method: :get, url: url, headers: headers}
|
137
|
+
execute(opts)
|
138
|
+
end
|
139
|
+
|
125
140
|
def volumes(id)
|
126
141
|
url = "#{@base_url}/api/servers/#{id}/volumes"
|
127
142
|
headers = { :params => {},:authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'morpheus/api/api_client'
|
2
|
+
|
3
|
+
class Morpheus::ServicePlansInterface < Morpheus::APIClient
|
4
|
+
def initialize(access_token, refresh_token,expires_at = nil, base_url=nil)
|
5
|
+
@access_token = access_token
|
6
|
+
@refresh_token = refresh_token
|
7
|
+
@base_url = base_url
|
8
|
+
@expires_at = expires_at
|
9
|
+
end
|
10
|
+
|
11
|
+
def list(params={})
|
12
|
+
url = "#{@base_url}/api/service-plans"
|
13
|
+
headers = { params: params, authorization: "Bearer #{@access_token}" }
|
14
|
+
execute(method: :get, url: url, headers: headers)
|
15
|
+
end
|
16
|
+
|
17
|
+
def get(options=nil)
|
18
|
+
url = "#{@base_url}/api/service-plans"
|
19
|
+
headers = { params: {}, authorization: "Bearer #{@access_token}" }
|
20
|
+
|
21
|
+
if options.is_a?(Hash)
|
22
|
+
headers[:params].merge!(options)
|
23
|
+
elsif options.is_a?(Numeric)
|
24
|
+
url = "#{@base_url}/api/service-plans/#{options}"
|
25
|
+
elsif options.is_a?(String)
|
26
|
+
headers[:params]['name'] = options
|
27
|
+
end
|
28
|
+
execute(method: :get, url: url, headers: headers)
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'morpheus/api/api_client'
|
2
|
+
|
3
|
+
class Morpheus::SubnetsInterface < Morpheus::APIClient
|
4
|
+
def initialize(access_token, refresh_token,expires_at = nil, base_url=nil)
|
5
|
+
@access_token = access_token
|
6
|
+
@refresh_token = refresh_token
|
7
|
+
@base_url = base_url
|
8
|
+
@expires_at = expires_at
|
9
|
+
end
|
10
|
+
|
11
|
+
def get(id, params={})
|
12
|
+
raise "#{self.class}.get() passed a blank id!" if id.to_s == ''
|
13
|
+
url = "#{@base_url}/api/subnets/#{id}"
|
14
|
+
headers = { params: params, authorization: "Bearer #{@access_token}" }
|
15
|
+
opts = {method: :get, url: url, headers: headers}
|
16
|
+
execute(opts)
|
17
|
+
end
|
18
|
+
|
19
|
+
def list(params={})
|
20
|
+
url = "#{@base_url}/api/subnets"
|
21
|
+
headers = { params: params, authorization: "Bearer #{@access_token}" }
|
22
|
+
opts = {method: :get, url: url, headers: headers}
|
23
|
+
execute(opts)
|
24
|
+
end
|
25
|
+
|
26
|
+
def create(network_id, payload)
|
27
|
+
url = "#{@base_url}/api/subnets"
|
28
|
+
headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
29
|
+
opts = {method: :post, url: url, headers: headers, payload: payload.to_json}
|
30
|
+
execute(opts)
|
31
|
+
end
|
32
|
+
|
33
|
+
def update(id, payload)
|
34
|
+
url = "#{@base_url}/api/subnets/#{id}"
|
35
|
+
headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
36
|
+
opts = {method: :put, url: url, headers: headers, payload: payload.to_json}
|
37
|
+
execute(opts)
|
38
|
+
end
|
39
|
+
|
40
|
+
def destroy(id, params={})
|
41
|
+
url = "#{@base_url}/api/subnets/#{id}"
|
42
|
+
headers = { :params => params, :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
43
|
+
opts = {method: :delete, url: url, headers: headers}
|
44
|
+
execute(opts)
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
data/lib/morpheus/cli.rb
CHANGED
@@ -99,6 +99,7 @@ module Morpheus
|
|
99
99
|
load 'morpheus/cli/instance_types.rb'
|
100
100
|
load 'morpheus/cli/security_groups.rb'
|
101
101
|
load 'morpheus/cli/security_group_rules.rb'
|
102
|
+
load 'morpheus/cli/clusters.rb'
|
102
103
|
load 'morpheus/cli/tenants_command.rb'
|
103
104
|
load 'morpheus/cli/accounts.rb'
|
104
105
|
load 'morpheus/cli/account_groups_command.rb'
|
data/lib/morpheus/cli/apps.rb
CHANGED
@@ -985,7 +985,7 @@ class Morpheus::Cli::Apps
|
|
985
985
|
options = {}
|
986
986
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
987
987
|
opts.banner = subcommand_usage("[app]")
|
988
|
-
build_common_options(opts, options, [:list, :json, :dry_run, :remote])
|
988
|
+
build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
|
989
989
|
opts.footer = "List logs for an app.\n" +
|
990
990
|
"[app] is required. This is the name or id of an app."
|
991
991
|
end
|
@@ -1005,27 +1005,28 @@ class Morpheus::Cli::Apps
|
|
1005
1005
|
end
|
1006
1006
|
end
|
1007
1007
|
params = {}
|
1008
|
-
|
1009
|
-
params[k] = options[k] unless options[k].nil?
|
1010
|
-
end
|
1008
|
+
params.merge!(parse_list_options(options))
|
1011
1009
|
params[:query] = params.delete(:phrase) unless params[:phrase].nil?
|
1012
|
-
@
|
1010
|
+
@logs_interface.setopts(options)
|
1013
1011
|
if options[:dry_run]
|
1014
1012
|
print_dry_run @logs_interface.dry.container_logs(containers, params)
|
1015
1013
|
return
|
1016
1014
|
end
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1015
|
+
json_response = @logs_interface.container_logs(containers, params)
|
1016
|
+
render_result = render_with_format(json_response, options, 'data')
|
1017
|
+
return 0 if render_result
|
1018
|
+
|
1019
|
+
logs = json_response
|
1020
|
+
title = "App Logs: #{app['name']}"
|
1021
|
+
subtitles = parse_list_subtitles(options)
|
1022
|
+
if params[:query]
|
1023
|
+
subtitles << "Search: #{params[:query]}".strip
|
1024
|
+
end
|
1025
|
+
# todo: startMs, endMs, sorts insteaad of sort..etc
|
1026
|
+
print_h1 title, subtitles, options
|
1027
|
+
if logs['data'].empty?
|
1028
|
+
puts "#{cyan}No logs found.#{reset}"
|
1021
1029
|
else
|
1022
|
-
title = "App Logs: #{app['name']}"
|
1023
|
-
subtitles = []
|
1024
|
-
if params[:query]
|
1025
|
-
subtitles << "Search: #{params[:query]}".strip
|
1026
|
-
end
|
1027
|
-
# todo: startMs, endMs, sorts insteaad of sort..etc
|
1028
|
-
print_h1 title, subtitles, options
|
1029
1030
|
logs['data'].reverse.each do |log_entry|
|
1030
1031
|
log_level = ''
|
1031
1032
|
case log_entry['level']
|
@@ -1042,9 +1043,10 @@ class Morpheus::Cli::Apps
|
|
1042
1043
|
end
|
1043
1044
|
puts "[#{log_entry['ts']}] #{log_level} - #{log_entry['message'].to_s.strip}"
|
1044
1045
|
end
|
1045
|
-
|
1046
|
-
return 0
|
1046
|
+
print_results_pagination({'meta'=>{'total'=>json_response['total'],'size'=>json_response['data'].size,'max'=>(json_response['max'] || options[:max]),'offset'=>(json_response['offset'] || options[:offset] || 0)}})
|
1047
1047
|
end
|
1048
|
+
print reset,"\n"
|
1049
|
+
return 0
|
1048
1050
|
rescue RestClient::Exception => e
|
1049
1051
|
print_rest_exception(e, options)
|
1050
1052
|
exit 1
|
@@ -937,7 +937,11 @@ module Morpheus
|
|
937
937
|
output = as_yaml(json_response, options, object_key)
|
938
938
|
elsif options[:csv]
|
939
939
|
row = object_key ? json_response[object_key] : json_response
|
940
|
-
|
940
|
+
if row.is_a?(Array)
|
941
|
+
output = records_as_csv(row, options)
|
942
|
+
else
|
943
|
+
output = records_as_csv([row], options)
|
944
|
+
end
|
941
945
|
end
|
942
946
|
if output
|
943
947
|
if options[:outfile]
|
@@ -0,0 +1,3952 @@
|
|
1
|
+
# require 'yaml'
|
2
|
+
require 'io/console'
|
3
|
+
require 'rest_client'
|
4
|
+
require 'optparse'
|
5
|
+
require 'filesize'
|
6
|
+
require 'morpheus/cli/cli_command'
|
7
|
+
#require 'morpheus/cli/mixins/infrastructure_helper'
|
8
|
+
|
9
|
+
class Morpheus::Cli::Clusters
|
10
|
+
include Morpheus::Cli::CliCommand
|
11
|
+
include Morpheus::Cli::ProvisioningHelper
|
12
|
+
include Morpheus::Cli::ProcessesHelper
|
13
|
+
include Morpheus::Cli::WhoamiHelper
|
14
|
+
include Morpheus::Cli::AccountsHelper
|
15
|
+
|
16
|
+
register_subcommands :list, :count, :get, :view, :add, :update, :remove, :logs, :history, {:'history-details' => :history_details}, {:'history-event' => :history_event_details}
|
17
|
+
register_subcommands :list_workers, :add_worker
|
18
|
+
register_subcommands :list_masters
|
19
|
+
register_subcommands :list_volumes, :remove_volume
|
20
|
+
register_subcommands :list_namespaces, :get_namespace, :add_namespace, :update_namespace, :remove_namespace
|
21
|
+
register_subcommands :list_containers, :remove_container, :restart_container
|
22
|
+
register_subcommands :list_deployments, :remove_deployment, :restart_deployment
|
23
|
+
register_subcommands :list_stateful_sets, :remove_stateful_set, :restart_stateful_set
|
24
|
+
register_subcommands :list_pods, :remove_pod, :restart_pod
|
25
|
+
register_subcommands :list_jobs, :remove_job
|
26
|
+
register_subcommands :list_services, :remove_service
|
27
|
+
register_subcommands :update_permissions
|
28
|
+
register_subcommands :api_config, :view_api_token, :view_kube_config
|
29
|
+
register_subcommands :wiki, :update_wiki
|
30
|
+
|
31
|
+
def connect(opts)
|
32
|
+
@api_client = establish_remote_appliance_connection(opts)
|
33
|
+
@clusters_interface = @api_client.clusters
|
34
|
+
@groups_interface = @api_client.groups
|
35
|
+
@compute_type_layouts_interface = @api_client.library_compute_type_layouts
|
36
|
+
@security_groups_interface = @api_client.security_groups
|
37
|
+
#@security_group_rules_interface = @api_client.security_group_rules
|
38
|
+
@cloud_resource_pools_interface = @api_client.cloud_resource_pools
|
39
|
+
@clouds_interface = @api_client.clouds
|
40
|
+
@servers_interface = @api_client.servers
|
41
|
+
@server_types_interface = @api_client.server_types
|
42
|
+
@options_interface = @api_client.options
|
43
|
+
@active_group_id = Morpheus::Cli::Groups.active_group
|
44
|
+
@provision_types_interface = @api_client.provision_types
|
45
|
+
@service_plans_interface = @api_client.service_plans
|
46
|
+
@user_groups_interface = @api_client.user_groups
|
47
|
+
@accounts_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).accounts
|
48
|
+
@logs_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).logs
|
49
|
+
#@active_security_group = ::Morpheus::Cli::SecurityGroups.load_security_group_file
|
50
|
+
end
|
51
|
+
|
52
|
+
def handle(args)
|
53
|
+
handle_subcommand(args)
|
54
|
+
end
|
55
|
+
|
56
|
+
def list(args)
|
57
|
+
options = {}
|
58
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
59
|
+
opts.banner = subcommand_usage()
|
60
|
+
build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
|
61
|
+
opts.footer = "List clusters."
|
62
|
+
end
|
63
|
+
optparse.parse!(args)
|
64
|
+
if args.count != 0
|
65
|
+
raise_command_error "wrong number of arguments, expected 0 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
|
66
|
+
end
|
67
|
+
connect(options)
|
68
|
+
begin
|
69
|
+
params = {}
|
70
|
+
params.merge!(parse_list_options(options))
|
71
|
+
@clusters_interface.setopts(options)
|
72
|
+
if options[:dry_run]
|
73
|
+
print_dry_run @clusters_interface.dry.list(params)
|
74
|
+
return
|
75
|
+
end
|
76
|
+
json_response = @clusters_interface.list(params)
|
77
|
+
|
78
|
+
render_result = render_with_format(json_response, options, 'clusters')
|
79
|
+
return 0 if render_result
|
80
|
+
|
81
|
+
title = "Morpheus Clusters"
|
82
|
+
subtitles = []
|
83
|
+
subtitles += parse_list_subtitles(options)
|
84
|
+
print_h1 title, subtitles
|
85
|
+
|
86
|
+
clusters = json_response['clusters']
|
87
|
+
|
88
|
+
if clusters.empty?
|
89
|
+
print yellow,"No clusters found.",reset,"\n"
|
90
|
+
else
|
91
|
+
print_clusters_table(clusters, options)
|
92
|
+
end
|
93
|
+
print reset,"\n"
|
94
|
+
return 0
|
95
|
+
rescue RestClient::Exception => e
|
96
|
+
print_rest_exception(e, options)
|
97
|
+
exit 1
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def count(args)
|
102
|
+
options = {}
|
103
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
104
|
+
opts.banner = subcommand_usage("[options]")
|
105
|
+
build_common_options(opts, options, [:query, :remote, :dry_run])
|
106
|
+
opts.footer = "Get the number of clusters."
|
107
|
+
end
|
108
|
+
optparse.parse!(args)
|
109
|
+
if args.count != 0
|
110
|
+
raise_command_error "wrong number of arguments, expected 0 and got (#{args.count}) #{args}\n#{optparse}"
|
111
|
+
end
|
112
|
+
connect(options)
|
113
|
+
begin
|
114
|
+
params = {}
|
115
|
+
params.merge!(parse_list_options(options))
|
116
|
+
@clusters_interface.setopts(options)
|
117
|
+
if options[:dry_run]
|
118
|
+
print_dry_run @clusters_interface.dry.get(params)
|
119
|
+
return
|
120
|
+
end
|
121
|
+
json_response = @clusters_interface.get(params)
|
122
|
+
# print number only
|
123
|
+
if json_response['meta'] && json_response['meta']['total']
|
124
|
+
print cyan, json_response['meta']['total'], reset, "\n"
|
125
|
+
else
|
126
|
+
print yellow, "unknown", reset, "\n"
|
127
|
+
end
|
128
|
+
rescue RestClient::Exception => e
|
129
|
+
print_rest_exception(e, options)
|
130
|
+
exit 1
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def get(args)
|
135
|
+
options = {}
|
136
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
137
|
+
opts.banner = subcommand_usage("[id]")
|
138
|
+
opts.on( nil, '--hosts', "Display masters and workers" ) do
|
139
|
+
options[:show_masters] = true
|
140
|
+
options[:show_workers] = true
|
141
|
+
end
|
142
|
+
opts.on( nil, '--masters', "Display masters" ) do
|
143
|
+
options[:show_masters] = true
|
144
|
+
end
|
145
|
+
opts.on( nil, '--workers', "Display workers" ) do
|
146
|
+
options[:show_workers] = true
|
147
|
+
end
|
148
|
+
opts.on( nil, '--permissions', "Display permissions" ) do
|
149
|
+
options[:show_perms] = true
|
150
|
+
end
|
151
|
+
opts.on('--refresh [SECONDS]', String, "Refresh until status is provisioned,failed. Default interval is #{default_refresh_interval} seconds.") do |val|
|
152
|
+
options[:refresh_until_status] ||= "provisioned,failed"
|
153
|
+
if !val.to_s.empty?
|
154
|
+
options[:refresh_interval] = val.to_f
|
155
|
+
end
|
156
|
+
end
|
157
|
+
opts.on('--refresh-until STATUS', String, "Refresh until a specified status is reached.") do |val|
|
158
|
+
options[:refresh_until_status] = val.to_s.downcase
|
159
|
+
end
|
160
|
+
build_common_options(opts, options, [:json, :dry_run, :remote])
|
161
|
+
opts.footer = "Get details about a cluster."
|
162
|
+
end
|
163
|
+
optparse.parse!(args)
|
164
|
+
if args.count < 1
|
165
|
+
raise_command_error "wrong number of arguments, expected 1-N and got (#{args.count}) #{args}\n#{optparse}"
|
166
|
+
end
|
167
|
+
connect(options)
|
168
|
+
id_list = parse_id_list(args)
|
169
|
+
return run_command_for_each_arg(id_list) do |arg|
|
170
|
+
_get(arg, options)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def _get(arg, options={})
|
175
|
+
|
176
|
+
begin
|
177
|
+
@clusters_interface.setopts(options)
|
178
|
+
|
179
|
+
if options[:dry_run]
|
180
|
+
if arg.to_s =~ /\A\d{1,}\Z/
|
181
|
+
print_dry_run @clusters_interface.dry.get(arg.to_i)
|
182
|
+
else
|
183
|
+
print_dry_run @clusters_interface.dry.list({name:arg})
|
184
|
+
end
|
185
|
+
return 0
|
186
|
+
end
|
187
|
+
cluster = find_cluster_by_name_or_id(arg)
|
188
|
+
return 1 if cluster.nil?
|
189
|
+
json_response = nil
|
190
|
+
if arg.to_s =~ /\A\d{1,}\Z/
|
191
|
+
json_response = {'cluster' => cluster} # skip redundant request
|
192
|
+
else
|
193
|
+
json_response = @clusters_interface.get(cluster['id'])
|
194
|
+
end
|
195
|
+
|
196
|
+
if options[:json]
|
197
|
+
print JSON.pretty_generate(json_response)
|
198
|
+
print "\n"
|
199
|
+
return
|
200
|
+
end
|
201
|
+
|
202
|
+
cluster = json_response['cluster']
|
203
|
+
worker_stats = cluster['workerStats']
|
204
|
+
clusterType = find_cluster_type_by_id(cluster['type']['id'])
|
205
|
+
|
206
|
+
print_h1 "Morpheus Cluster"
|
207
|
+
print cyan
|
208
|
+
description_cols = {
|
209
|
+
"ID" => 'id',
|
210
|
+
"Name" => 'name',
|
211
|
+
"Type" => lambda { |it| it['type']['name'] },
|
212
|
+
#"Group" => lambda { |it| it['site']['name'] },
|
213
|
+
"Cloud" => lambda { |it| it['zone']['name'] },
|
214
|
+
"Location" => lambda { |it| it['location'] },
|
215
|
+
"Layout" => lambda { |it| it['layout'] ? it['layout']['name'] : ''},
|
216
|
+
"API Url" => 'serviceUrl',
|
217
|
+
"Visibility" => lambda { |it| it['visibility'].to_s.capitalize },
|
218
|
+
#"Groups" => lambda {|it| it['groups'].collect {|g| g.instance_of?(Hash) ? g['name'] : g.to_s }.join(', ') },
|
219
|
+
#"Owner" => lambda {|it| it['owner'].instance_of?(Hash) ? it['owner']['name'] : it['ownerId'] },
|
220
|
+
#"Tenant" => lambda {|it| it['account'].instance_of?(Hash) ? it['account']['name'] : it['accountId'] },
|
221
|
+
"Created" => lambda {|it| format_local_dt(it['dateCreated']) },
|
222
|
+
"Created By" => lambda {|it| it['createdBy'] ? it['createdBy']['username'] : '' },
|
223
|
+
"Enabled" => lambda { |it| format_boolean(it['enabled']) },
|
224
|
+
"Status" => lambda { |it| format_cluster_status(it) }
|
225
|
+
}
|
226
|
+
print_description_list(description_cols, cluster)
|
227
|
+
|
228
|
+
print_h2 "Cluster Details"
|
229
|
+
print cyan
|
230
|
+
|
231
|
+
print "Namespaces: #{cluster['namespacesCount']}".center(20) if !cluster['namespacesCount'].nil?
|
232
|
+
print "Masters: #{cluster['mastersCount']}".center(20) if !cluster['mastersCount'].nil?
|
233
|
+
print "Workers: #{cluster['workersCount']}".center(20) if !clusterType['workersCount'].nil?
|
234
|
+
print "Volumes: #{cluster['volumesCount']}".center(20) if !cluster['volumesCount'].nil?
|
235
|
+
print "Containers: #{cluster['containersCount']}".center(20) if !cluster['containersCount'].nil?
|
236
|
+
print "Services: #{cluster['servicesCount']}".center(20) if !cluster['servicesCount'].nil?
|
237
|
+
print "Jobs: #{cluster['jobsCount']}".center(20) if !cluster['jobsCount'].nil?
|
238
|
+
print "Pods: #{cluster['podsCount']}".center(20) if !cluster['podsCount'].nil?
|
239
|
+
print "Deployments: #{cluster['deploymentsCount']}".center(20) if !cluster['deploymentsCount'].nil?
|
240
|
+
print "\n"
|
241
|
+
|
242
|
+
if options[:show_masters]
|
243
|
+
masters_json = @clusters_interface.list_masters(cluster['id'], options)
|
244
|
+
if masters_json.nil? || masters_json['masters'].empty?
|
245
|
+
print_h2 "Masters"
|
246
|
+
print yellow,"No masters found.",reset,"\n"
|
247
|
+
else
|
248
|
+
masters = masters_json['masters']
|
249
|
+
print_h2 "Masters"
|
250
|
+
rows = masters.collect do |server|
|
251
|
+
{
|
252
|
+
id: server['id'],
|
253
|
+
name: server['name'],
|
254
|
+
type: (server['type']['name'] rescue server['type']),
|
255
|
+
status: format_server_status(server),
|
256
|
+
power: format_server_power_state(server, cyan)
|
257
|
+
}
|
258
|
+
end
|
259
|
+
columns = [:id, :name, :status, :power]
|
260
|
+
puts as_pretty_table(rows, columns, options)
|
261
|
+
end
|
262
|
+
end
|
263
|
+
if options[:show_workers]
|
264
|
+
workers_json = @clusters_interface.list_workers(cluster['id'], options)
|
265
|
+
if workers_json.nil? || workers_json['workers'].empty?
|
266
|
+
print_h2 "Workers"
|
267
|
+
print yellow,"No workers found.",reset,"\n"
|
268
|
+
else
|
269
|
+
workers = workers_json['workers']
|
270
|
+
print_h2 "Workers"
|
271
|
+
rows = workers.collect do |server|
|
272
|
+
{
|
273
|
+
id: server['id'],
|
274
|
+
name: server['name'],
|
275
|
+
type: (server['type']['name'] rescue server['type']),
|
276
|
+
status: format_server_status(server),
|
277
|
+
power: format_server_power_state(server, cyan)
|
278
|
+
}
|
279
|
+
end
|
280
|
+
columns = [:id, :name, :status, :power]
|
281
|
+
puts as_pretty_table(rows, columns, options)
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
if worker_stats
|
286
|
+
print_h2 "Worker Usage"
|
287
|
+
print cyan
|
288
|
+
# print "CPU Usage: #{worker_stats['cpuUsage']}".center(20)
|
289
|
+
# print "Memory: #{worker_stats['usedMemory']}".center(20)
|
290
|
+
# print "Storage: #{worker_stats['usedStorage']}".center(20)
|
291
|
+
print_stats_usage(worker_stats, {include: [:max_cpu, :avg_cpu, :memory, :storage]})
|
292
|
+
print reset,"\n"
|
293
|
+
end
|
294
|
+
|
295
|
+
if options[:show_perms]
|
296
|
+
permissions = cluster['permissions']
|
297
|
+
print_permissions(permissions)
|
298
|
+
end
|
299
|
+
|
300
|
+
# refresh until a status is reached
|
301
|
+
if options[:refresh_until_status]
|
302
|
+
if options[:refresh_interval].nil? || options[:refresh_interval].to_f < 0
|
303
|
+
options[:refresh_interval] = default_refresh_interval
|
304
|
+
end
|
305
|
+
statuses = options[:refresh_until_status].to_s.downcase.split(",").collect {|s| s.strip }.select {|s| !s.to_s.empty? }
|
306
|
+
if !statuses.include?(cluster['status'])
|
307
|
+
print cyan, "Refreshing in #{options[:refresh_interval] > 1 ? options[:refresh_interval].to_i : options[:refresh_interval]} seconds"
|
308
|
+
sleep_with_dots(options[:refresh_interval])
|
309
|
+
print "\n"
|
310
|
+
_get(arg, options)
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
return 0
|
315
|
+
rescue RestClient::Exception => e
|
316
|
+
print_rest_exception(e, options)
|
317
|
+
exit 1
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
def view(args)
|
322
|
+
options = {}
|
323
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
324
|
+
opts.banner = subcommand_usage("[cluster]")
|
325
|
+
opts.on('-w','--wiki', "Open the wiki tab for this cluster") do
|
326
|
+
options[:link_tab] = "wiki"
|
327
|
+
end
|
328
|
+
opts.on('--tab VALUE', String, "Open a specific tab") do |val|
|
329
|
+
options[:link_tab] = val.to_s
|
330
|
+
end
|
331
|
+
build_common_options(opts, options, [:dry_run, :remote])
|
332
|
+
opts.footer = "View a cluster in a web browser" + "\n" +
|
333
|
+
"[cluster] is required. This is the name or id of a cluster. Supports 1-N [cluster] arguments."
|
334
|
+
end
|
335
|
+
optparse.parse!(args)
|
336
|
+
if args.count < 1
|
337
|
+
raise_command_error "wrong number of arguments, expected 1-N and got (#{args.count}) #{args}\n#{optparse}"
|
338
|
+
end
|
339
|
+
connect(options)
|
340
|
+
id_list = parse_id_list(args)
|
341
|
+
return run_command_for_each_arg(id_list) do |arg|
|
342
|
+
_view(arg, options)
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
|
347
|
+
def _view(arg, options={})
|
348
|
+
begin
|
349
|
+
cluster = find_cluster_by_name_or_id(arg)
|
350
|
+
return 1 if cluster.nil?
|
351
|
+
|
352
|
+
link = "#{@appliance_url}/login/oauth-redirect?access_token=#{@access_token}\\&redirectUri=/infrastructure/clusters/#{cluster['id']}"
|
353
|
+
if options[:link_tab]
|
354
|
+
link << "#!#{options[:link_tab]}"
|
355
|
+
end
|
356
|
+
|
357
|
+
open_command = nil
|
358
|
+
if RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/
|
359
|
+
open_command = "start #{link}"
|
360
|
+
elsif RbConfig::CONFIG['host_os'] =~ /darwin/
|
361
|
+
open_command = "open #{link}"
|
362
|
+
elsif RbConfig::CONFIG['host_os'] =~ /linux|bsd/
|
363
|
+
open_command = "xdg-open #{link}"
|
364
|
+
end
|
365
|
+
|
366
|
+
if options[:dry_run]
|
367
|
+
puts "system: #{open_command}"
|
368
|
+
return 0
|
369
|
+
end
|
370
|
+
|
371
|
+
system(open_command)
|
372
|
+
|
373
|
+
return 0
|
374
|
+
rescue RestClient::Exception => e
|
375
|
+
print_rest_exception(e, options)
|
376
|
+
exit 1
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
380
|
+
def add(args)
|
381
|
+
options = {}
|
382
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
383
|
+
opts.banner = subcommand_usage( "[name]")
|
384
|
+
opts.on( '--name NAME', "Cluster Name" ) do |val|
|
385
|
+
options[:name] = val.to_s
|
386
|
+
end
|
387
|
+
opts.on("--description [TEXT]", String, "Description") do |val|
|
388
|
+
options[:description] = val.to_s
|
389
|
+
end
|
390
|
+
opts.on( '--resource-name NAME', "Resource Name" ) do |val|
|
391
|
+
options[:resourceName] = val.to_s
|
392
|
+
end
|
393
|
+
opts.on('--tags LIST', String, "Tags") do |val|
|
394
|
+
options[:tags] = val
|
395
|
+
end
|
396
|
+
opts.on( '-g', '--group GROUP', "Group Name or ID" ) do |val|
|
397
|
+
options[:group] = val
|
398
|
+
end
|
399
|
+
opts.on( '-t', '--cluster-type TYPE', "Cluster Type Name or ID" ) do |val|
|
400
|
+
options[:clusterType] = val
|
401
|
+
end
|
402
|
+
opts.on( '-l', '--layout LAYOUT', "Layout Name or ID" ) do |val|
|
403
|
+
options[:layout] = val
|
404
|
+
end
|
405
|
+
opts.on('--visibility [private|public]', String, "Visibility") do |val|
|
406
|
+
options[:visibility] = val
|
407
|
+
end
|
408
|
+
opts.on('--refresh [SECONDS]', String, "Refresh until status is provisioned,failed. Default interval is #{default_refresh_interval} seconds.") do |val|
|
409
|
+
options[:refresh_interval] = val.to_s.empty? ? default_refresh_interval : val.to_f
|
410
|
+
end
|
411
|
+
opts.on('--workflow ID', String, "Workflow") do |val|
|
412
|
+
params['taskSetId'] = val.to_i
|
413
|
+
end
|
414
|
+
add_server_options(opts, options)
|
415
|
+
build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote])
|
416
|
+
opts.footer = "Create a cluster.\n" +
|
417
|
+
"[name] is required. This is the name of the new cluster."
|
418
|
+
end
|
419
|
+
|
420
|
+
optparse.parse!(args)
|
421
|
+
if args.count > 1
|
422
|
+
raise_command_error "wrong number of arguments, expected 0-2 and got (#{args.count}) #{args}\n#{optparse}"
|
423
|
+
end
|
424
|
+
connect(options)
|
425
|
+
|
426
|
+
begin
|
427
|
+
payload = nil
|
428
|
+
if options[:payload]
|
429
|
+
payload = options[:payload]
|
430
|
+
# support -O OPTION switch on top of --payload
|
431
|
+
payload['cluster'] ||= {}
|
432
|
+
if options[:options]
|
433
|
+
payload['cluster'].deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) })
|
434
|
+
end
|
435
|
+
if args[0]
|
436
|
+
payload['cluster']['name'] = args[0]
|
437
|
+
elsif options[:name]
|
438
|
+
payload['cluster']['name'] = options[:name]
|
439
|
+
end
|
440
|
+
# if args[1]
|
441
|
+
# payload['cluster']['description'] = args[1]
|
442
|
+
# elsif options[:description]
|
443
|
+
# payload['cluster']['description'] = options[:description]
|
444
|
+
# end
|
445
|
+
payload['cluster']['server'] ||= {}
|
446
|
+
if options[:resourceName]
|
447
|
+
payload['cluster']['server']['name'] = options[:resourceName]
|
448
|
+
end
|
449
|
+
if options[:description]
|
450
|
+
payload['cluster']['description'] = options[:description]
|
451
|
+
end
|
452
|
+
else
|
453
|
+
cluster_payload = {}
|
454
|
+
server_payload = {"config" => {}}
|
455
|
+
|
456
|
+
# Cluster Type
|
457
|
+
cluster_type_id = nil
|
458
|
+
cluster_type = options[:clusterType] ? find_cluster_type_by_name_or_id(options[:clusterType]) : nil
|
459
|
+
|
460
|
+
if cluster_type
|
461
|
+
cluster_type_id = cluster_type['id']
|
462
|
+
else
|
463
|
+
available_cluster_types = cluster_types_for_dropdown
|
464
|
+
|
465
|
+
if available_cluster_types.empty?
|
466
|
+
print_red_alert "A cluster type is required"
|
467
|
+
exit 1
|
468
|
+
elsif available_cluster_types.count > 1 && !options[:no_prompt]
|
469
|
+
cluster_type_code = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'clusterType', 'type' => 'select', 'fieldLabel' => 'Cluster Type', 'selectOptions' => cluster_types_for_dropdown, 'required' => true, 'description' => 'Select Cluster Type.'}],options[:options],@api_client,{})['clusterType']
|
470
|
+
else
|
471
|
+
cluster_type_code = available_cluster_types.first['code']
|
472
|
+
end
|
473
|
+
cluster_type = get_cluster_types.find { |ct| ct['code'] == cluster_type_code }
|
474
|
+
end
|
475
|
+
|
476
|
+
cluster_payload['type'] = cluster_type['code'] # {'id' => cluster_type['id']}
|
477
|
+
|
478
|
+
# Cluster Name
|
479
|
+
if args.empty? && options[:no_prompt]
|
480
|
+
print_red_alert "No cluster name provided"
|
481
|
+
exit 1
|
482
|
+
elsif !args.empty?
|
483
|
+
cluster_payload['name'] = args[0]
|
484
|
+
elsif options[:name]
|
485
|
+
cluster_payload['name'] = options[:name]
|
486
|
+
else
|
487
|
+
existing_cluster_names = @clusters_interface.list()['clusters'].collect { |cluster| cluster['name'] }
|
488
|
+
while cluster_payload['name'].empty?
|
489
|
+
name = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'name', 'type' => 'text', 'fieldLabel' => 'Cluster Name', 'required' => true, 'description' => 'Cluster Name.'}],options[:options],@api_client,{})['name']
|
490
|
+
|
491
|
+
if existing_cluster_names.include?(name)
|
492
|
+
print_red_alert "Name must be unique, #{name} already exists"
|
493
|
+
else
|
494
|
+
cluster_payload['name'] = name
|
495
|
+
end
|
496
|
+
end
|
497
|
+
end
|
498
|
+
|
499
|
+
# Cluster Description
|
500
|
+
# if !args.empty? && args.count > 1
|
501
|
+
# cluster_payload['description'] = args[1]
|
502
|
+
# elsif options[:description]
|
503
|
+
# cluster_payload['description'] = options[:description]
|
504
|
+
# elsif !options[:no_prompt]
|
505
|
+
# cluster_payload['description'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'description', 'type' => 'text', 'fieldLabel' => 'Cluster Description', 'required' => false, 'description' => 'Cluster Description.'}],options[:options],@api_client,{})['description']
|
506
|
+
# end
|
507
|
+
|
508
|
+
# Resource Name
|
509
|
+
resourceName = options[:resourceName]
|
510
|
+
|
511
|
+
if !resourceName
|
512
|
+
if options[:no_prompt]
|
513
|
+
print_red_alert "No resource name provided"
|
514
|
+
exit 1
|
515
|
+
else
|
516
|
+
resourceName = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'resourceName', 'type' => 'text', 'fieldLabel' => 'Resource Name', 'required' => true, 'description' => 'Resource Name.'}],options[:options],@api_client,{})['resourceName']
|
517
|
+
end
|
518
|
+
end
|
519
|
+
|
520
|
+
server_payload['name'] = resourceName
|
521
|
+
|
522
|
+
# Resource Description
|
523
|
+
description = options[:description] || Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'description', 'type' => 'text', 'fieldLabel' => 'Description', 'required' => false, 'description' => 'Resource Description.'}],options[:options],@api_client,{})['description']
|
524
|
+
cluster_payload['description'] = description if description
|
525
|
+
|
526
|
+
tags = options[:tags]
|
527
|
+
|
528
|
+
if !tags && !options[:no_prompt]
|
529
|
+
tags = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'tags', 'type' => 'text', 'fieldLabel' => 'Resource Tags', 'required' => false, 'description' => 'Resource Tags.'}],options[:options],@api_client,{})['tags']
|
530
|
+
end
|
531
|
+
|
532
|
+
server_payload['tags'] = tags if tags
|
533
|
+
|
534
|
+
# Group / Site
|
535
|
+
group = load_group(options)
|
536
|
+
cluster_payload['group'] = {'id' => group['id']}
|
537
|
+
|
538
|
+
# Cloud / Zone
|
539
|
+
cloud_id = nil
|
540
|
+
cloud = options[:cloud] ? find_cloud_by_name_or_id_for_provisioning(group['id'], options[:cloud]) : nil
|
541
|
+
if cloud
|
542
|
+
# load full cloud
|
543
|
+
cloud = @clouds_interface.get(cloud['id'])['zone']
|
544
|
+
cloud_id = cloud['id']
|
545
|
+
else
|
546
|
+
available_clouds = get_available_clouds(group['id'])
|
547
|
+
|
548
|
+
if available_clouds.empty?
|
549
|
+
print_red_alert "Group #{group['name']} has no available clouds"
|
550
|
+
exit 1
|
551
|
+
else
|
552
|
+
cloud_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'cloud', 'type' => 'select', 'fieldLabel' => 'Cloud', 'selectOptions' => available_clouds, 'required' => true, 'description' => 'Select Cloud.'}],options[:options],@api_client,{groupId: group['id']})['cloud']
|
553
|
+
end
|
554
|
+
cloud = @clouds_interface.get(cloud_id)['zone']
|
555
|
+
end
|
556
|
+
|
557
|
+
cloud['zoneType'] = get_cloud_type(cloud['zoneType']['id'])
|
558
|
+
cluster_payload['cloud'] = {'id' => cloud['id']}
|
559
|
+
|
560
|
+
# Layout
|
561
|
+
layout = options[:layout] ? find_layout_by_name_or_id(options[:layout]) : nil
|
562
|
+
|
563
|
+
if !layout
|
564
|
+
available_layouts = layouts_for_dropdown(cloud['id'], cluster_type['id'])
|
565
|
+
|
566
|
+
if !available_layouts.empty?
|
567
|
+
if available_layouts.count > 1 && !options[:no_prompt]
|
568
|
+
layout_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'layout', 'type' => 'select', 'fieldLabel' => 'Layout', 'selectOptions' => available_layouts, 'required' => true, 'description' => 'Select Layout.'}],options[:options],@api_client,{})['layout']
|
569
|
+
else
|
570
|
+
layout_id = available_layouts.first['id']
|
571
|
+
end
|
572
|
+
layout = find_layout_by_name_or_id(layout_id)
|
573
|
+
end
|
574
|
+
end
|
575
|
+
|
576
|
+
cluster_payload['layout'] = {id: layout['id']}
|
577
|
+
|
578
|
+
# Plan
|
579
|
+
provision_type = (layout && layout['provisionType'] ? layout['provisionType'] : nil) || get_provision_type_for_zone_type(cloud['zoneType']['id'])
|
580
|
+
service_plan = prompt_service_plan(cloud['id'], provision_type, options)
|
581
|
+
|
582
|
+
if service_plan
|
583
|
+
server_payload['plan'] = {'id' => service_plan['id'], 'code' => service_plan['code'], 'options' => prompt_service_plan_options(service_plan, options)}
|
584
|
+
end
|
585
|
+
|
586
|
+
# Controller type
|
587
|
+
server_types = @server_types_interface.list({max:1, computeTypeId: cluster_type['controllerTypes'].first['id'], zoneTypeId: cloud['zoneType']['id'], useZoneProvisionTypes: true})['serverTypes']
|
588
|
+
controller_provision_type = nil
|
589
|
+
resource_pool = nil
|
590
|
+
|
591
|
+
if !server_types.empty?
|
592
|
+
controller_type = server_types.first
|
593
|
+
controller_provision_type = controller_type['provisionType'] ? (@provision_types_interface.get(controller_type['provisionType']['id'])['provisionType'] rescue nil) : nil
|
594
|
+
|
595
|
+
if controller_provision_type && resource_pool = prompt_resource_pool(group, cloud, service_plan, controller_provision_type, options)
|
596
|
+
server_payload['config']['resourcePool'] = resource_pool['externalId']
|
597
|
+
end
|
598
|
+
end
|
599
|
+
|
600
|
+
# Multi-disk / prompt for volumes
|
601
|
+
volumes = options[:volumes] || prompt_volumes(service_plan, options.merge({'defaultAddFirstDataVolume': true}), @api_client, {zoneId: cloud['id'], siteId: group['id']})
|
602
|
+
|
603
|
+
if !volumes.empty?
|
604
|
+
server_payload['volumes'] = volumes
|
605
|
+
end
|
606
|
+
|
607
|
+
# Networks
|
608
|
+
# NOTE: You must choose subnets in the same availability zone
|
609
|
+
if controller_provision_type && controller_provision_type['hasNetworks'] && cloud['zoneType']['code'] != 'esxi'
|
610
|
+
server_payload['networkInterfaces'] = options[:networkInterfaces] || prompt_network_interfaces(cloud['id'], provision_type['id'], (resource_pool['id'] rescue nil), options)
|
611
|
+
end
|
612
|
+
|
613
|
+
# Security Groups
|
614
|
+
server_payload['securityGroups'] = prompt_security_groups_by_cloud(cloud, provision_type, resource_pool, options)
|
615
|
+
|
616
|
+
# Visibility
|
617
|
+
server_payload['visibility'] = options[:visibility] || (Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'visibility', 'fieldLabel' => 'Visibility', 'type' => 'select', 'defaultValue' => 'private', 'required' => true, 'selectOptions' => [{'name' => 'Private', 'value' => 'private'},{'name' => 'Public', 'value' => 'public'}]}], options[:options], @api_client, {})['visibility'])
|
618
|
+
|
619
|
+
# Options / Custom Config
|
620
|
+
option_type_list = ((controller_type['optionTypes'].reject { |type| !type['enabled'] || type['fieldComponent'] } rescue []) + layout['optionTypes'] +
|
621
|
+
(cluster_type['optionTypes'].reject { |type| !type['enabled'] || !type['creatable'] || type['fieldComponent'] } rescue [])).sort { |type| type['displayOrder'] }
|
622
|
+
|
623
|
+
# remove volume options if volumes were configured
|
624
|
+
if !server_payload['volumes'].empty?
|
625
|
+
option_type_list = reject_volume_option_types(option_type_list)
|
626
|
+
end
|
627
|
+
# remove networkId option if networks were configured above
|
628
|
+
if !server_payload['networkInterfaces'].empty?
|
629
|
+
option_type_list = reject_networking_option_types(option_type_list)
|
630
|
+
end
|
631
|
+
|
632
|
+
server_payload.deep_merge!(Morpheus::Cli::OptionTypes.prompt(option_type_list, options[:options], @api_client, {zoneId: cloud['id'], siteId: group['id'], layoutId: layout['id']}))
|
633
|
+
|
634
|
+
# Create User
|
635
|
+
if !options[:createUser].nil?
|
636
|
+
server_payload['config']['createUser'] = options[:createUser]
|
637
|
+
elsif !options[:no_prompt]
|
638
|
+
current_user['windowsUsername'] || current_user['linuxUsername']
|
639
|
+
server_payload['config']['createUser'] = (current_user['windowsUsername'] || current_user['linuxUsername']) && Morpheus::Cli::OptionTypes.confirm("Create Your User?", {:default => true})
|
640
|
+
end
|
641
|
+
|
642
|
+
# User Groups
|
643
|
+
userGroup = options[:userGroup] ? find_user_group_by_name_or_id(nil, options[:userGroup]) : nil
|
644
|
+
|
645
|
+
if userGroup
|
646
|
+
server_payload['userGroup'] = userGroup
|
647
|
+
elsif !options[:no_prompt]
|
648
|
+
userGroupId = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'userGroupId', 'fieldLabel' => 'User Group', 'type' => 'select', 'required' => false, 'optionSource' => 'userGroups'}], options[:options], @api_client, {})['userGroupId']
|
649
|
+
|
650
|
+
if userGroupId
|
651
|
+
server_payload['userGroup'] = {'id' => userGroupId}
|
652
|
+
end
|
653
|
+
end
|
654
|
+
|
655
|
+
# Host / Domain
|
656
|
+
server_payload['networkDomain'] = options[:domain] || Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'networkDomain', 'fieldLabel' => 'Network Domain', 'type' => 'select', 'required' => false, 'optionSource' => 'networkDomains'}], options[:options], @api_client, {})['networkDomain']
|
657
|
+
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)['hostname']
|
658
|
+
|
659
|
+
# Workflow / Automation
|
660
|
+
task_set_id = options[:taskSetId] || Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'taskSet', 'fieldLabel' => 'Workflow', 'type' => 'select', 'required' => false, 'optionSource' => 'taskSets'}], options[:options], @api_client, {'phase' => 'postProvision'})['taskSet']
|
661
|
+
|
662
|
+
if !task_set_id.nil?
|
663
|
+
server_payload['taskSet'] = {'id' => task_set_id}
|
664
|
+
end
|
665
|
+
|
666
|
+
cluster_payload['server'] = server_payload
|
667
|
+
payload = {'cluster' => cluster_payload}
|
668
|
+
end
|
669
|
+
@clusters_interface.setopts(options)
|
670
|
+
if options[:dry_run]
|
671
|
+
print_dry_run @clusters_interface.dry.create(payload)
|
672
|
+
return
|
673
|
+
end
|
674
|
+
json_response = @clusters_interface.create(payload)
|
675
|
+
if options[:json]
|
676
|
+
print JSON.pretty_generate(json_response)
|
677
|
+
print "\n"
|
678
|
+
elsif json_response['success']
|
679
|
+
get_args = [json_response["cluster"]["id"]] + (options[:remote] ? ["-r",options[:remote]] : []) + (options[:refresh_interval] ? ['--refresh', options[:refresh_interval].to_s] : [])
|
680
|
+
get(get_args)
|
681
|
+
else
|
682
|
+
print_rest_errors(json_response, options)
|
683
|
+
end
|
684
|
+
rescue RestClient::Exception => e
|
685
|
+
print_rest_exception(e, options)
|
686
|
+
exit 1
|
687
|
+
end
|
688
|
+
end
|
689
|
+
|
690
|
+
def update(args)
|
691
|
+
options = {}
|
692
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
693
|
+
opts.banner = subcommand_usage( "[cluster] --name --description --active")
|
694
|
+
opts.on("--name NAME", String, "Updates Cluster Name") do |val|
|
695
|
+
options[:name] = val.to_s
|
696
|
+
end
|
697
|
+
opts.on("--description [TEXT]", String, "Updates Cluster Description") do |val|
|
698
|
+
options[:description] = val.to_s
|
699
|
+
end
|
700
|
+
opts.on("--api-url [TEXT]", String, "Updates Cluster API Url") do |val|
|
701
|
+
options[:apiUrl] = val.to_s
|
702
|
+
end
|
703
|
+
opts.on('--active [on|off]', String, "Can be used to enable / disable the cluster. Default is on") do |val|
|
704
|
+
options[:active] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
|
705
|
+
end
|
706
|
+
opts.on( nil, '--refresh', "Refresh cluster" ) do
|
707
|
+
options[:refresh] = true
|
708
|
+
end
|
709
|
+
opts.on("--tenant ACCOUNT", String, "Account ID or Name" ) do |val|
|
710
|
+
options[:tenant] = val
|
711
|
+
end
|
712
|
+
build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote])
|
713
|
+
opts.footer = "Update a cluster.\n" +
|
714
|
+
"[cluster] is required. This is the name or id of an existing cluster."
|
715
|
+
end
|
716
|
+
|
717
|
+
optparse.parse!(args)
|
718
|
+
if args.count != 1
|
719
|
+
raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args}\n#{optparse}"
|
720
|
+
end
|
721
|
+
connect(options)
|
722
|
+
|
723
|
+
begin
|
724
|
+
payload = nil
|
725
|
+
cluster = nil
|
726
|
+
|
727
|
+
if options[:payload]
|
728
|
+
payload = options[:payload]
|
729
|
+
# support -O OPTION switch on top of --payload
|
730
|
+
if options[:options]
|
731
|
+
payload['cluster'] ||= {}
|
732
|
+
payload['cluster'].deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) })
|
733
|
+
end
|
734
|
+
|
735
|
+
if !payload['cluster'].empty?
|
736
|
+
cluster = find_cluster_by_name_or_id(payload['cluster']['id'] || payload['cluster']['name'])
|
737
|
+
end
|
738
|
+
else
|
739
|
+
cluster = find_cluster_by_name_or_id(args[0])
|
740
|
+
cluster_payload = {}
|
741
|
+
cluster_payload['name'] = options[:name] if !options[:name].empty?
|
742
|
+
cluster_payload['description'] = options[:description] if !options[:description].empty?
|
743
|
+
cluster_payload['enabled'] = options[:active] if !options[:active].nil?
|
744
|
+
cluster_payload['serviceUrl'] = options[:apiUrl] if !options[:apiUrl].nil?
|
745
|
+
cluster_payload['refresh'] = options[:refresh] if options[:refresh] == true
|
746
|
+
cluster_payload['tenant'] = options[:tenant] if !options[:tenant].nil?
|
747
|
+
payload = {"cluster" => cluster_payload}
|
748
|
+
end
|
749
|
+
|
750
|
+
if !cluster
|
751
|
+
print_red_alert "No clusters available for update"
|
752
|
+
exit 1
|
753
|
+
end
|
754
|
+
|
755
|
+
has_field_updates = ['name', 'description', 'enabled', 'serviceUrl'].find {|field| payload['cluster'] && !payload['cluster'][field].nil? && payload['cluster'][field] != cluster[field] ? field : nil}
|
756
|
+
|
757
|
+
if !has_field_updates && cluster_payload['refresh'].nil? && cluster_payload['tenant'].nil?
|
758
|
+
print_green_success "Nothing to update"
|
759
|
+
exit 1
|
760
|
+
end
|
761
|
+
|
762
|
+
@clusters_interface.setopts(options)
|
763
|
+
if options[:dry_run]
|
764
|
+
print_dry_run @clusters_interface.dry.update(cluster['id'], payload)
|
765
|
+
return
|
766
|
+
end
|
767
|
+
json_response = @clusters_interface.update(cluster['id'], payload)
|
768
|
+
if options[:json]
|
769
|
+
print JSON.pretty_generate(json_response)
|
770
|
+
print "\n"
|
771
|
+
elsif json_response['success']
|
772
|
+
get_args = [json_response["cluster"]["id"]] + (options[:remote] ? ["-r",options[:remote]] : []) + (options[:refresh_interval] ? ['--refresh', options[:refresh_interval].to_s] : [])
|
773
|
+
get(get_args)
|
774
|
+
else
|
775
|
+
print_rest_errors(json_response, options)
|
776
|
+
end
|
777
|
+
end
|
778
|
+
end
|
779
|
+
|
780
|
+
def remove(args)
|
781
|
+
options = {}
|
782
|
+
query_params = {:removeResources => 'on'}
|
783
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
784
|
+
opts.banner = subcommand_usage("[cluster]")
|
785
|
+
opts.on('--remove-resources [on|off]', ['on','off'], "Remove Infrastructure. Default is on.") do |val|
|
786
|
+
query_params[:removeResources] = val.nil? ? 'on' : val
|
787
|
+
end
|
788
|
+
opts.on('--preserve-volumes [on|off]', ['on','off'], "Preserve Volumes. Default is off.") do |val|
|
789
|
+
query_params[:preserveVolumes] = val.nil? ? 'on' : val
|
790
|
+
end
|
791
|
+
opts.on('--remove-instances [on|off]', ['on','off'], "Remove Associated Instances. Default is off.") do |val|
|
792
|
+
query_params[:removeInstances] = val.nil? ? 'on' : val
|
793
|
+
end
|
794
|
+
opts.on('--release-eips [on|off]', ['on','off'], "Release EIPs, default is on. Amazon only.") do |val|
|
795
|
+
params[:releaseEIPs] = val.nil? ? 'on' : val
|
796
|
+
end
|
797
|
+
opts.on( '-f', '--force', "Force Delete" ) do
|
798
|
+
query_params[:force] = 'on'
|
799
|
+
end
|
800
|
+
build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :quiet, :remote])
|
801
|
+
opts.footer = "Delete a cluster.\n" +
|
802
|
+
"[cluster] is required. This is the name or id of an existing cluster."
|
803
|
+
end
|
804
|
+
optparse.parse!(args)
|
805
|
+
if args.count != 1
|
806
|
+
raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args}\n#{optparse}"
|
807
|
+
end
|
808
|
+
connect(options)
|
809
|
+
|
810
|
+
begin
|
811
|
+
cluster = find_cluster_by_name_or_id(args[0])
|
812
|
+
|
813
|
+
unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to remove the cluster '#{cluster['name']}'?", options)
|
814
|
+
return 9, "aborted command"
|
815
|
+
end
|
816
|
+
@clusters_interface.setopts(options)
|
817
|
+
if options[:dry_run]
|
818
|
+
print_dry_run @clusters_interface.dry.destroy(cluster['id'], query_params)
|
819
|
+
return
|
820
|
+
end
|
821
|
+
json_response = @clusters_interface.destroy(cluster['id'], query_params)
|
822
|
+
if options[:json]
|
823
|
+
print JSON.pretty_generate(json_response)
|
824
|
+
print "\n"
|
825
|
+
elsif !options[:quiet]
|
826
|
+
print_green_success "Cluster #{cluster['name']} is being removed..."
|
827
|
+
#list([])
|
828
|
+
end
|
829
|
+
rescue RestClient::Exception => e
|
830
|
+
print_rest_exception(e, options)
|
831
|
+
exit 1
|
832
|
+
end
|
833
|
+
end
|
834
|
+
|
835
|
+
def logs(args)
|
836
|
+
options = {}
|
837
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
838
|
+
opts.banner = subcommand_usage("[cluster]")
|
839
|
+
build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
|
840
|
+
end
|
841
|
+
optparse.parse!(args)
|
842
|
+
if args.count != 1
|
843
|
+
raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args}\n#{optparse}"
|
844
|
+
end
|
845
|
+
connect(options)
|
846
|
+
begin
|
847
|
+
cluster = find_cluster_by_name_or_id(args[0])
|
848
|
+
params = {}
|
849
|
+
params.merge!(parse_list_options(options))
|
850
|
+
params[:query] = params.delete(:phrase) unless params[:phrase].nil?
|
851
|
+
@logs_interface.setopts(options)
|
852
|
+
if options[:dry_run]
|
853
|
+
print_dry_run @logs_interface.dry.cluster_logs(cluster['id'], params)
|
854
|
+
return
|
855
|
+
end
|
856
|
+
json_response = @logs_interface.cluster_logs(cluster['id'], params)
|
857
|
+
render_result = render_with_format(json_response, options, 'data')
|
858
|
+
return 0 if render_result
|
859
|
+
|
860
|
+
logs = json_response
|
861
|
+
title = "Cluster Logs: #{cluster['name']}"
|
862
|
+
subtitles = parse_list_subtitles(options)
|
863
|
+
if params[:query]
|
864
|
+
subtitles << "Search: #{params[:query]}".strip
|
865
|
+
end
|
866
|
+
# todo: startMs, endMs, sorts insteaad of sort..etc
|
867
|
+
print_h1 title, subtitles, options
|
868
|
+
if logs['data'].empty?
|
869
|
+
puts "#{cyan}No logs found.#{reset}"
|
870
|
+
else
|
871
|
+
logs['data'].reverse.each do |log_entry|
|
872
|
+
log_level = ''
|
873
|
+
case log_entry['level']
|
874
|
+
when 'INFO'
|
875
|
+
log_level = "#{blue}#{bold}INFO#{reset}"
|
876
|
+
when 'DEBUG'
|
877
|
+
log_level = "#{white}#{bold}DEBUG#{reset}"
|
878
|
+
when 'WARN'
|
879
|
+
log_level = "#{yellow}#{bold}WARN#{reset}"
|
880
|
+
when 'ERROR'
|
881
|
+
log_level = "#{red}#{bold}ERROR#{reset}"
|
882
|
+
when 'FATAL'
|
883
|
+
log_level = "#{red}#{bold}FATAL#{reset}"
|
884
|
+
end
|
885
|
+
puts "[#{log_entry['ts']}] #{log_level} - #{log_entry['message'].to_s.strip}"
|
886
|
+
end
|
887
|
+
print_results_pagination({'meta'=>{'total'=>json_response['total'],'size'=>json_response['data'].size,'max'=>(json_response['max'] || options[:max]),'offset'=>(json_response['offset'] || options[:offset] || 0)}})
|
888
|
+
end
|
889
|
+
print reset, "\n"
|
890
|
+
return 0
|
891
|
+
rescue RestClient::Exception => e
|
892
|
+
print_rest_exception(e, options)
|
893
|
+
exit 1
|
894
|
+
end
|
895
|
+
end
|
896
|
+
|
897
|
+
def update_permissions(args)
|
898
|
+
options = {}
|
899
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
900
|
+
opts.banner = subcommand_usage( "[cluster]")
|
901
|
+
add_perms_options(opts, options)
|
902
|
+
build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote])
|
903
|
+
opts.footer = "Update a clusters permissions.\n" +
|
904
|
+
"[cluster] is required. This is the name or id of an existing cluster."
|
905
|
+
end
|
906
|
+
|
907
|
+
optparse.parse!(args)
|
908
|
+
if args.count != 1
|
909
|
+
raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args}\n#{optparse}"
|
910
|
+
end
|
911
|
+
connect(options)
|
912
|
+
|
913
|
+
begin
|
914
|
+
cluster = find_cluster_by_name_or_id(args[0])
|
915
|
+
return 1 if cluster.nil?
|
916
|
+
|
917
|
+
if options[:payload]
|
918
|
+
payload = options[:payload]
|
919
|
+
# support -O OPTION switch on top of --payload
|
920
|
+
if options[:options]
|
921
|
+
payload['permissions'] ||= {}
|
922
|
+
payload['permissions'].deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) })
|
923
|
+
end
|
924
|
+
else
|
925
|
+
payload = {"permissions" => prompt_permissions(options)}
|
926
|
+
end
|
927
|
+
|
928
|
+
@clusters_interface.setopts(options)
|
929
|
+
if options[:dry_run]
|
930
|
+
print_dry_run @clusters_interface.dry.update_permissions(cluster['id'], payload)
|
931
|
+
return
|
932
|
+
end
|
933
|
+
json_response = @clusters_interface.update_permissions(cluster['id'], payload)
|
934
|
+
if options[:json]
|
935
|
+
print JSON.pretty_generate(json_response)
|
936
|
+
print "\n"
|
937
|
+
elsif json_response['success']
|
938
|
+
get_args = [json_response["cluster"]["id"], '--permissions'] + (options[:remote] ? ["-r",options[:remote]] : []) + (options[:refresh_interval] ? ['--refresh', options[:refresh_interval].to_s] : [])
|
939
|
+
get(get_args)
|
940
|
+
else
|
941
|
+
print_rest_errors(json_response, options)
|
942
|
+
end
|
943
|
+
end
|
944
|
+
end
|
945
|
+
|
946
|
+
def list_workers(args)
|
947
|
+
options = {}
|
948
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
949
|
+
opts.banner = subcommand_usage( "[cluster]")
|
950
|
+
build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
|
951
|
+
opts.footer = "List workers for a cluster.\n" +
|
952
|
+
"[cluster] is required. This is the name or id of an existing cluster."
|
953
|
+
end
|
954
|
+
|
955
|
+
optparse.parse!(args)
|
956
|
+
if args.count != 1
|
957
|
+
raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
|
958
|
+
end
|
959
|
+
connect(options)
|
960
|
+
begin
|
961
|
+
cluster = find_cluster_by_name_or_id(args[0])
|
962
|
+
return 1 if cluster.nil?
|
963
|
+
|
964
|
+
params = {}
|
965
|
+
params.merge!(parse_list_options(options))
|
966
|
+
@clusters_interface.setopts(options)
|
967
|
+
if options[:dry_run]
|
968
|
+
print_dry_run @clusters_interface.dry.list_workers(cluster['id'], params)
|
969
|
+
return
|
970
|
+
end
|
971
|
+
json_response = @clusters_interface.list_workers(cluster['id'], params)
|
972
|
+
|
973
|
+
render_result = render_with_format(json_response, options, 'workers')
|
974
|
+
return 0 if render_result
|
975
|
+
|
976
|
+
title = "Morpheus Cluster Workers: #{cluster['name']}"
|
977
|
+
subtitles = []
|
978
|
+
subtitles += parse_list_subtitles(options)
|
979
|
+
print_h1 title, subtitles
|
980
|
+
workers = json_response['workers']
|
981
|
+
if workers.empty?
|
982
|
+
print yellow,"No workers found.",reset,"\n"
|
983
|
+
else
|
984
|
+
# more stuff to show here
|
985
|
+
|
986
|
+
servers = workers
|
987
|
+
multi_tenant = false
|
988
|
+
|
989
|
+
# print_servers_table(servers)
|
990
|
+
rows = servers.collect {|server|
|
991
|
+
stats = server['stats']
|
992
|
+
|
993
|
+
if !stats['maxMemory']
|
994
|
+
stats['maxMemory'] = stats['usedMemory'] + stats['freeMemory']
|
995
|
+
end
|
996
|
+
cpu_usage_str = !stats ? "" : generate_usage_bar((stats['cpuUsage']).to_f, 100, {max_bars: 10})
|
997
|
+
memory_usage_str = !stats ? "" : generate_usage_bar(stats['usedMemory'], stats['maxMemory'], {max_bars: 10})
|
998
|
+
storage_usage_str = !stats ? "" : generate_usage_bar(stats['usedStorage'], stats['maxStorage'], {max_bars: 10})
|
999
|
+
if options[:details]
|
1000
|
+
if stats['maxMemory'] && stats['maxMemory'].to_i != 0
|
1001
|
+
memory_usage_str = memory_usage_str + cyan + format_bytes_short(stats['usedMemory']).strip.rjust(8, ' ') + " / " + format_bytes_short(stats['maxMemory']).strip
|
1002
|
+
end
|
1003
|
+
if stats['maxStorage'] && stats['maxStorage'].to_i != 0
|
1004
|
+
storage_usage_str = storage_usage_str + cyan + format_bytes_short(stats['usedStorage']).strip.rjust(8, ' ') + " / " + format_bytes_short(stats['maxStorage']).strip
|
1005
|
+
end
|
1006
|
+
end
|
1007
|
+
row = {
|
1008
|
+
id: server['id'],
|
1009
|
+
tenant: server['account'] ? server['account']['name'] : server['accountId'],
|
1010
|
+
name: server['name'],
|
1011
|
+
platform: server['serverOs'] ? server['serverOs']['name'].upcase : 'N/A',
|
1012
|
+
cloud: server['zone'] ? server['zone']['name'] : '',
|
1013
|
+
type: server['computeServerType'] ? server['computeServerType']['name'] : 'unmanaged',
|
1014
|
+
nodes: server['containers'] ? server['containers'].size : '',
|
1015
|
+
status: format_server_status(server, cyan),
|
1016
|
+
power: format_server_power_state(server, cyan),
|
1017
|
+
cpu: cpu_usage_str + cyan,
|
1018
|
+
memory: memory_usage_str + cyan,
|
1019
|
+
storage: storage_usage_str + cyan
|
1020
|
+
}
|
1021
|
+
row
|
1022
|
+
}
|
1023
|
+
columns = [:id, :name, :type, :cloud, :nodes, :status, :power]
|
1024
|
+
if multi_tenant
|
1025
|
+
columns.insert(4, :tenant)
|
1026
|
+
end
|
1027
|
+
columns += [:cpu, :memory, :storage]
|
1028
|
+
# custom pretty table columns ...
|
1029
|
+
if options[:include_fields]
|
1030
|
+
columns = options[:include_fields]
|
1031
|
+
end
|
1032
|
+
print cyan
|
1033
|
+
print as_pretty_table(rows, columns, options)
|
1034
|
+
print reset
|
1035
|
+
print_results_pagination(json_response)
|
1036
|
+
end
|
1037
|
+
print reset,"\n"
|
1038
|
+
return 0
|
1039
|
+
rescue RestClient::Exception => e
|
1040
|
+
print_rest_exception(e, options)
|
1041
|
+
exit 1
|
1042
|
+
end
|
1043
|
+
end
|
1044
|
+
|
1045
|
+
def add_worker(args)
|
1046
|
+
options = {}
|
1047
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
1048
|
+
opts.banner = subcommand_usage( "[cluster] [options]")
|
1049
|
+
opts.on("--name NAME", String, "Name of the new worker") do |val|
|
1050
|
+
options[:name] = val.to_s
|
1051
|
+
end
|
1052
|
+
opts.on("--description [TEXT]", String, "Description") do |val|
|
1053
|
+
options[:description] = val.to_s
|
1054
|
+
end
|
1055
|
+
add_server_options(opts, options)
|
1056
|
+
build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote])
|
1057
|
+
opts.footer = "Add worker to a cluster.\n" +
|
1058
|
+
"[cluster] is required. This is the name or id of an existing cluster.\n" +
|
1059
|
+
"[name] is required. This is the name of the new worker."
|
1060
|
+
end
|
1061
|
+
|
1062
|
+
optparse.parse!(args)
|
1063
|
+
if args.count != 1
|
1064
|
+
raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args}\n#{optparse}"
|
1065
|
+
end
|
1066
|
+
connect(options)
|
1067
|
+
|
1068
|
+
begin
|
1069
|
+
cluster = find_cluster_by_name_or_id(args[0])
|
1070
|
+
return 1 if cluster.nil?
|
1071
|
+
payload = nil
|
1072
|
+
if options[:payload]
|
1073
|
+
payload = options[:payload]
|
1074
|
+
# support -O OPTION switch on top of --payload
|
1075
|
+
if options[:options]
|
1076
|
+
payload['server'] ||= {}
|
1077
|
+
payload['server'].deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) })
|
1078
|
+
end
|
1079
|
+
else
|
1080
|
+
server_payload = {'config' => {}}
|
1081
|
+
|
1082
|
+
# If not available add set type return
|
1083
|
+
layout = find_layout_by_id(cluster['layout']['id'])
|
1084
|
+
|
1085
|
+
# currently limiting to just worker types
|
1086
|
+
available_type_sets = layout['computeServers'].reject {|typeSet| !typeSet['dynamicCount']}
|
1087
|
+
|
1088
|
+
if available_type_sets.empty?
|
1089
|
+
print_red_alert "Cluster #{cluster['name']} has no available server types to add"
|
1090
|
+
exit 1
|
1091
|
+
else
|
1092
|
+
type_set = available_type_sets[0]
|
1093
|
+
end
|
1094
|
+
|
1095
|
+
# find servers within cluster
|
1096
|
+
server_matches = cluster['servers'].reject {|server| server['typeSet']['id'] != type_set['id']}
|
1097
|
+
server_type = find_server_type_by_id(server_matches.count > 0 ? server_matches[0]['computeServerType']['id'] : type_set['computeServerType']['id'])
|
1098
|
+
server_payload['serverType'] = {'id' => server_type['id']}
|
1099
|
+
|
1100
|
+
# Name
|
1101
|
+
if options[:name].empty?
|
1102
|
+
default_name = (server_matches.count ? server_matches[0]['name'] : nil) || cluster['name']
|
1103
|
+
default_name.delete_prefix!(type_set['namePrefix']) if !type_set['namePrefix'].empty?
|
1104
|
+
default_name = default_name[0..(default_name.index(type_set['nameSuffix']) - 1)] if !type_set['nameSuffix'].nil? && default_name.index(type_set['nameSuffix'])
|
1105
|
+
default_name = (type_set['namePrefix'] || '') + default_name + (type_set['nameSuffix'] || '') + '-' + (server_matches.count + 1).to_s
|
1106
|
+
server_payload['name'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'name', 'type' => 'text', 'fieldLabel' => 'Name', 'required' => true, 'description' => 'Worker Name.', 'defaultValue' => default_name}],options[:options],@api_client,{})['name']
|
1107
|
+
else
|
1108
|
+
server_payload['name'] = options[:name]
|
1109
|
+
end
|
1110
|
+
|
1111
|
+
# Description
|
1112
|
+
server_payload['description'] = options[:description] || Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text', 'description' => 'Worker Description'}], options[:options], @api_client)['description']
|
1113
|
+
|
1114
|
+
# Cloud
|
1115
|
+
available_clouds = options_interface.options_for_source('clouds', {groupId: cluster['site']['id'], clusterId: cluster['id'], ownerOnly: true})['data']
|
1116
|
+
cloud_id = nil
|
1117
|
+
|
1118
|
+
if options[:cloud]
|
1119
|
+
cloud = available_clouds.find {|it| it['value'].to_s == options[:cloud].to_s || it['name'].casecmp?(options[:cloud].to_s)}
|
1120
|
+
|
1121
|
+
if !cloud
|
1122
|
+
print_red_alert "Cloud #{options[:cloud]} is not a valid option for cluster #{cluster['name']}"
|
1123
|
+
exit 1
|
1124
|
+
end
|
1125
|
+
cloud_id = cloud['value']
|
1126
|
+
end
|
1127
|
+
|
1128
|
+
if !cloud_id
|
1129
|
+
default_cloud = available_clouds.find {|it| it['value'] == cluster['zone']['id']}
|
1130
|
+
cloud_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'cloud', 'fieldLabel' => 'Cloud', 'type' => 'select', 'selectOptions' => available_clouds, 'description' => 'Cloud', 'required' => true, 'defaultValue' => (default_cloud ? default_cloud['name'] : nil)}], options[:options], @api_client)['cloud']
|
1131
|
+
cloud_id = (default_cloud && cloud_id == default_cloud['name']) ? default_cloud['value'] : cloud_id
|
1132
|
+
end
|
1133
|
+
|
1134
|
+
server_payload['cloud'] = {'id' => cloud_id}
|
1135
|
+
service_plan = prompt_service_plan(cloud_id, server_type['provisionType'], options)
|
1136
|
+
|
1137
|
+
if service_plan
|
1138
|
+
server_payload['plan'] = {'code' => service_plan['code'], 'options' => prompt_service_plan_options(service_plan, options)}
|
1139
|
+
end
|
1140
|
+
|
1141
|
+
# resources (zone pools)
|
1142
|
+
cloud = @clouds_interface.get(cloud_id)['zone']
|
1143
|
+
cloud['zoneType'] = get_cloud_type(cloud['zoneType']['id'])
|
1144
|
+
group = @groups_interface.get(cluster['site']['id'])['group']
|
1145
|
+
|
1146
|
+
if resource_pool = prompt_resource_pool(cluster, cloud, service_plan, server_type['provisionType'], options)
|
1147
|
+
server_payload['config']['resourcePool'] = resource_pool['externalId']
|
1148
|
+
end
|
1149
|
+
|
1150
|
+
# Multi-disk / prompt for volumes
|
1151
|
+
volumes = options[:volumes] || prompt_volumes(service_plan, options.merge({'defaultAddFirstDataVolume': true}), @api_client, {zoneId: cloud['id'], siteId: group['id']})
|
1152
|
+
|
1153
|
+
if !volumes.empty?
|
1154
|
+
server_payload['volumes'] = volumes
|
1155
|
+
end
|
1156
|
+
|
1157
|
+
# Networks
|
1158
|
+
# NOTE: You must choose subnets in the same availability zone
|
1159
|
+
provision_type = server_type['provisionType'] || {}
|
1160
|
+
if provision_type && cloud['zoneType']['code'] != 'esxi'
|
1161
|
+
server_payload['networkInterfaces'] = options[:networkInterfaces] || prompt_network_interfaces(cloud['id'], server_type['provisionType']['id'], (resource_pool['id'] rescue nil), options)
|
1162
|
+
end
|
1163
|
+
|
1164
|
+
# Security Groups
|
1165
|
+
server_payload['securityGroups'] = prompt_security_groups_by_cloud(cloud, provision_type, resource_pool, options)
|
1166
|
+
|
1167
|
+
# Options / Custom Config
|
1168
|
+
option_type_list = (server_type['optionTypes'].reject { |type|
|
1169
|
+
!type['enabled'] || type['fieldComponent'] ||
|
1170
|
+
(['provisionType.vmware.host', 'provisionType.scvmm.host'].include?(type['code']) && cloud['config']['hideHostSelection'] == 'on') || # should this be truthy?
|
1171
|
+
(type['fieldContext'] == 'instance.networkDomain' && type['fieldName'] == 'id')
|
1172
|
+
} rescue [])
|
1173
|
+
|
1174
|
+
# remove volume options if volumes were configured
|
1175
|
+
if !server_payload['volumes'].empty?
|
1176
|
+
option_type_list = reject_volume_option_types(option_type_list)
|
1177
|
+
end
|
1178
|
+
# remove networkId option if networks were configured above
|
1179
|
+
if !server_payload['networkInterfaces'].empty?
|
1180
|
+
option_type_list = reject_networking_option_types(option_type_list)
|
1181
|
+
end
|
1182
|
+
|
1183
|
+
server_payload.deep_merge!(Morpheus::Cli::OptionTypes.prompt(option_type_list, options[:options], @api_client, {zoneId: cloud['id'], siteId: group['id'], layoutId: layout['id']}))
|
1184
|
+
|
1185
|
+
# Create User
|
1186
|
+
if !options[:createUser].nil?
|
1187
|
+
server_payload['config']['createUser'] = options[:createUser]
|
1188
|
+
elsif !options[:no_prompt]
|
1189
|
+
server_payload['config']['createUser'] = (current_user['windowsUsername'] || current_user['linuxUsername']) && Morpheus::Cli::OptionTypes.confirm("Create Your User?", {:default => true})
|
1190
|
+
end
|
1191
|
+
|
1192
|
+
# User Groups
|
1193
|
+
userGroup = options[:userGroup] ? find_user_group_by_name_or_id(nil, options[:userGroup]) : nil
|
1194
|
+
|
1195
|
+
if userGroup
|
1196
|
+
server_payload['userGroup'] = userGroup
|
1197
|
+
elsif !options[:no_prompt]
|
1198
|
+
userGroupId = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'userGroupId', 'fieldLabel' => 'User Group', 'type' => 'select', 'required' => false, 'optionSource' => 'userGroups'}], options[:options], @api_client, {})['userGroupId']
|
1199
|
+
|
1200
|
+
if userGroupId
|
1201
|
+
server_payload['userGroup'] = {'id' => userGroupId}
|
1202
|
+
end
|
1203
|
+
end
|
1204
|
+
|
1205
|
+
# Host / Domain
|
1206
|
+
server_payload['networkDomain'] = options[:domain] || Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'networkDomain', 'fieldLabel' => 'Network Domain', 'type' => 'select', 'required' => false, 'optionSource' => 'networkDomains'}], options[:options], @api_client, {})['networkDomain']
|
1207
|
+
server_payload['hostname'] = options[:hostname] || Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'hostname', 'fieldLabel' => 'Hostname', 'type' => 'text', 'required' => true, 'description' => 'Hostname', 'defaultValue' => server_payload['name']}], options[:options], @api_client)['hostname']
|
1208
|
+
|
1209
|
+
# Workflow / Automation
|
1210
|
+
if !options[:no_prompt]
|
1211
|
+
task_set_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'taskSet', 'fieldLabel' => 'Workflow', 'type' => 'select', 'required' => false, 'optionSource' => 'taskSets'}], options[:options], @api_client, {'phase' => 'postProvision', 'taskSetType' => 'provision'})['taskSet']
|
1212
|
+
|
1213
|
+
if task_set_id
|
1214
|
+
server_payload['taskSet'] = {'id' => task_set_id}
|
1215
|
+
end
|
1216
|
+
end
|
1217
|
+
payload = {'server' => server_payload}
|
1218
|
+
end
|
1219
|
+
|
1220
|
+
@clusters_interface.setopts(options)
|
1221
|
+
if options[:dry_run]
|
1222
|
+
print_dry_run @clusters_interface.dry.add_server(cluster['id'], payload)
|
1223
|
+
return
|
1224
|
+
end
|
1225
|
+
json_response = @clusters_interface.add_server(cluster['id'], payload)
|
1226
|
+
if options[:json]
|
1227
|
+
puts as_json(json_response)
|
1228
|
+
elsif json_response['success']
|
1229
|
+
print_green_success "Added worker to cluster #{cluster['name']}"
|
1230
|
+
#get_args = [json_response["cluster"]["id"]] + (options[:remote] ? ["-r",options[:remote]] : [])
|
1231
|
+
#get(get_args)
|
1232
|
+
end
|
1233
|
+
return 0
|
1234
|
+
rescue RestClient::Exception => e
|
1235
|
+
print_rest_exception(e, options)
|
1236
|
+
exit 1
|
1237
|
+
end
|
1238
|
+
end
|
1239
|
+
|
1240
|
+
def list_masters(args)
|
1241
|
+
options = {}
|
1242
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
1243
|
+
opts.banner = subcommand_usage( "[cluster]")
|
1244
|
+
build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
|
1245
|
+
opts.footer = "List masters for a cluster.\n" +
|
1246
|
+
"[cluster] is required. This is the name or id of an existing cluster."
|
1247
|
+
end
|
1248
|
+
|
1249
|
+
optparse.parse!(args)
|
1250
|
+
if args.count != 1
|
1251
|
+
raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
|
1252
|
+
end
|
1253
|
+
connect(options)
|
1254
|
+
begin
|
1255
|
+
cluster = find_cluster_by_name_or_id(args[0])
|
1256
|
+
return 1 if cluster.nil?
|
1257
|
+
|
1258
|
+
params = {}
|
1259
|
+
params.merge!(parse_list_options(options))
|
1260
|
+
@clusters_interface.setopts(options)
|
1261
|
+
if options[:dry_run]
|
1262
|
+
print_dry_run @clusters_interface.dry.list_masters(cluster['id'], params)
|
1263
|
+
return
|
1264
|
+
end
|
1265
|
+
json_response = @clusters_interface.list_masters(cluster['id'], params)
|
1266
|
+
|
1267
|
+
render_result = render_with_format(json_response, options, 'masters')
|
1268
|
+
return 0 if render_result
|
1269
|
+
|
1270
|
+
title = "Morpheus Cluster Masters: #{cluster['name']}"
|
1271
|
+
subtitles = []
|
1272
|
+
subtitles += parse_list_subtitles(options)
|
1273
|
+
print_h1 title, subtitles
|
1274
|
+
masters = json_response['masters']
|
1275
|
+
if masters.empty?
|
1276
|
+
print yellow,"No masters found.",reset,"\n"
|
1277
|
+
else
|
1278
|
+
# more stuff to show here
|
1279
|
+
|
1280
|
+
servers = masters
|
1281
|
+
multi_tenant = false
|
1282
|
+
|
1283
|
+
# print_servers_table(servers)
|
1284
|
+
rows = servers.collect {|server|
|
1285
|
+
stats = server['stats']
|
1286
|
+
|
1287
|
+
if !stats['maxMemory']
|
1288
|
+
stats['maxMemory'] = stats['usedMemory'] + stats['freeMemory']
|
1289
|
+
end
|
1290
|
+
cpu_usage_str = !stats ? "" : generate_usage_bar((stats['cpuUsage']).to_f, 100, {max_bars: 10})
|
1291
|
+
memory_usage_str = !stats ? "" : generate_usage_bar(stats['usedMemory'], stats['maxMemory'], {max_bars: 10})
|
1292
|
+
storage_usage_str = !stats ? "" : generate_usage_bar(stats['usedStorage'], stats['maxStorage'], {max_bars: 10})
|
1293
|
+
if options[:details]
|
1294
|
+
if stats['maxMemory'] && stats['maxMemory'].to_i != 0
|
1295
|
+
memory_usage_str = memory_usage_str + cyan + format_bytes_short(stats['usedMemory']).strip.rjust(8, ' ') + " / " + format_bytes_short(stats['maxMemory']).strip
|
1296
|
+
end
|
1297
|
+
if stats['maxStorage'] && stats['maxStorage'].to_i != 0
|
1298
|
+
storage_usage_str = storage_usage_str + cyan + format_bytes_short(stats['usedStorage']).strip.rjust(8, ' ') + " / " + format_bytes_short(stats['maxStorage']).strip
|
1299
|
+
end
|
1300
|
+
end
|
1301
|
+
row = {
|
1302
|
+
id: server['id'],
|
1303
|
+
tenant: server['account'] ? server['account']['name'] : server['accountId'],
|
1304
|
+
name: server['name'],
|
1305
|
+
platform: server['serverOs'] ? server['serverOs']['name'].upcase : 'N/A',
|
1306
|
+
cloud: server['zone'] ? server['zone']['name'] : '',
|
1307
|
+
type: server['computeServerType'] ? server['computeServerType']['name'] : 'unmanaged',
|
1308
|
+
nodes: server['containers'] ? server['containers'].size : '',
|
1309
|
+
status: format_server_status(server, cyan),
|
1310
|
+
power: format_server_power_state(server, cyan),
|
1311
|
+
cpu: cpu_usage_str + cyan,
|
1312
|
+
memory: memory_usage_str + cyan,
|
1313
|
+
storage: storage_usage_str + cyan
|
1314
|
+
}
|
1315
|
+
row
|
1316
|
+
}
|
1317
|
+
columns = [:id, :name, :type, :cloud, :nodes, :status, :power]
|
1318
|
+
if multi_tenant
|
1319
|
+
columns.insert(4, :tenant)
|
1320
|
+
end
|
1321
|
+
columns += [:cpu, :memory, :storage]
|
1322
|
+
# custom pretty table columns ...
|
1323
|
+
if options[:include_fields]
|
1324
|
+
columns = options[:include_fields]
|
1325
|
+
end
|
1326
|
+
print cyan
|
1327
|
+
print as_pretty_table(rows, columns, options)
|
1328
|
+
print reset
|
1329
|
+
print_results_pagination(json_response)
|
1330
|
+
end
|
1331
|
+
print reset,"\n"
|
1332
|
+
return 0
|
1333
|
+
rescue RestClient::Exception => e
|
1334
|
+
print_rest_exception(e, options)
|
1335
|
+
exit 1
|
1336
|
+
end
|
1337
|
+
end
|
1338
|
+
|
1339
|
+
def list_volumes(args)
|
1340
|
+
options = {}
|
1341
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
1342
|
+
opts.banner = subcommand_usage( "[cluster]")
|
1343
|
+
build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
|
1344
|
+
opts.footer = "List volumes for a cluster.\n" +
|
1345
|
+
"[cluster] is required. This is the name or id of an existing cluster."
|
1346
|
+
end
|
1347
|
+
|
1348
|
+
optparse.parse!(args)
|
1349
|
+
if args.count != 1
|
1350
|
+
raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
|
1351
|
+
end
|
1352
|
+
connect(options)
|
1353
|
+
begin
|
1354
|
+
cluster = find_cluster_by_name_or_id(args[0])
|
1355
|
+
return 1 if cluster.nil?
|
1356
|
+
|
1357
|
+
params = {}
|
1358
|
+
params.merge!(parse_list_options(options))
|
1359
|
+
@clusters_interface.setopts(options)
|
1360
|
+
if options[:dry_run]
|
1361
|
+
print_dry_run @clusters_interface.dry.list_volumes(cluster['id'], params)
|
1362
|
+
return
|
1363
|
+
end
|
1364
|
+
json_response = @clusters_interface.list_volumes(cluster['id'], params)
|
1365
|
+
|
1366
|
+
render_result = render_with_format(json_response, options, 'volumes')
|
1367
|
+
return 0 if render_result
|
1368
|
+
|
1369
|
+
title = "Morpheus Cluster Volumes: #{cluster['name']}"
|
1370
|
+
subtitles = []
|
1371
|
+
subtitles += parse_list_subtitles(options)
|
1372
|
+
print_h1 title, subtitles
|
1373
|
+
volumes = json_response['volumes']
|
1374
|
+
if volumes.empty?
|
1375
|
+
print yellow,"No volumes found.",reset,"\n"
|
1376
|
+
else
|
1377
|
+
# more stuff to show here
|
1378
|
+
rows = volumes.collect do |ns|
|
1379
|
+
{
|
1380
|
+
id: ns['id'],
|
1381
|
+
name: ns['name'],
|
1382
|
+
description: ns['description'],
|
1383
|
+
status: ns['status'],
|
1384
|
+
active: ns['active'],
|
1385
|
+
cluster: cluster['name']
|
1386
|
+
}
|
1387
|
+
end
|
1388
|
+
columns = [
|
1389
|
+
:id, :name, :description, :status, :active, :cluster => lambda { |it| cluster['name'] }
|
1390
|
+
]
|
1391
|
+
print as_pretty_table(rows, columns, options)
|
1392
|
+
end
|
1393
|
+
print reset,"\n"
|
1394
|
+
return 0
|
1395
|
+
rescue RestClient::Exception => e
|
1396
|
+
print_rest_exception(e, options)
|
1397
|
+
exit 1
|
1398
|
+
end
|
1399
|
+
end
|
1400
|
+
|
1401
|
+
def remove_volume(args)
|
1402
|
+
params = {}
|
1403
|
+
options = {}
|
1404
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
1405
|
+
opts.banner = subcommand_usage("[cluster] [volume]")
|
1406
|
+
build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :quiet, :remote])
|
1407
|
+
opts.footer = "Delete a volume within a cluster.\n" +
|
1408
|
+
"[cluster] is required. This is the name or id of an existing cluster.\n" +
|
1409
|
+
"[volume] is required. This is the name or id of an existing volume."
|
1410
|
+
end
|
1411
|
+
optparse.parse!(args)
|
1412
|
+
if args.count != 2
|
1413
|
+
raise_command_error "wrong number of arguments, expected 2 and got (#{args.count}) #{args}\n#{optparse}"
|
1414
|
+
end
|
1415
|
+
connect(options)
|
1416
|
+
|
1417
|
+
begin
|
1418
|
+
cluster = find_cluster_by_name_or_id(args[0])
|
1419
|
+
return 1 if cluster.nil?
|
1420
|
+
volume_id = args[1]
|
1421
|
+
|
1422
|
+
if volume_id.empty?
|
1423
|
+
raise_command_error "missing required volume parameter"
|
1424
|
+
end
|
1425
|
+
|
1426
|
+
volume = find_volume_by_name_or_id(cluster['id'], volume_id)
|
1427
|
+
if volume.nil?
|
1428
|
+
print_red_alert "Volume not found for '#{volume_id}'"
|
1429
|
+
return 1
|
1430
|
+
end
|
1431
|
+
unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to remove the cluster volume '#{volume['name'] || volume['id']}'?", options)
|
1432
|
+
return 9, "aborted command"
|
1433
|
+
end
|
1434
|
+
|
1435
|
+
@clusters_interface.setopts(options)
|
1436
|
+
if options[:dry_run]
|
1437
|
+
print_dry_run @clusters_interface.dry.destroy_volume(cluster['id'], volume['id'], params)
|
1438
|
+
return
|
1439
|
+
end
|
1440
|
+
json_response = @clusters_interface.destroy_volume(cluster['id'], volume['id'], params)
|
1441
|
+
if options[:json]
|
1442
|
+
print JSON.pretty_generate(json_response)
|
1443
|
+
print "\n"
|
1444
|
+
elsif !options[:quiet]
|
1445
|
+
print_red_alert "Error removing volume #{volume['name']} from cluster #{cluster['name']}: #{json_response['msg']}" if json_response['success'] == false
|
1446
|
+
print_green_success "Volume #{volume['name']} is being removed from cluster #{cluster['name']}..." if json_response['success'] == true
|
1447
|
+
end
|
1448
|
+
rescue RestClient::Exception => e
|
1449
|
+
print_rest_exception(e, options)
|
1450
|
+
exit 1
|
1451
|
+
end
|
1452
|
+
end
|
1453
|
+
|
1454
|
+
def list_services(args)
|
1455
|
+
options = {}
|
1456
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
1457
|
+
opts.banner = subcommand_usage( "[cluster]")
|
1458
|
+
build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
|
1459
|
+
opts.footer = "List services for a cluster.\n" +
|
1460
|
+
"[cluster] is required. This is the name or id of an existing cluster."
|
1461
|
+
end
|
1462
|
+
|
1463
|
+
optparse.parse!(args)
|
1464
|
+
if args.count != 1
|
1465
|
+
raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
|
1466
|
+
end
|
1467
|
+
connect(options)
|
1468
|
+
begin
|
1469
|
+
cluster = find_cluster_by_name_or_id(args[0])
|
1470
|
+
return 1 if cluster.nil?
|
1471
|
+
|
1472
|
+
params = {}
|
1473
|
+
params.merge!(parse_list_options(options))
|
1474
|
+
@clusters_interface.setopts(options)
|
1475
|
+
if options[:dry_run]
|
1476
|
+
print_dry_run @clusters_interface.dry.list_services(cluster['id'], params)
|
1477
|
+
return
|
1478
|
+
end
|
1479
|
+
json_response = @clusters_interface.list_services(cluster['id'], params)
|
1480
|
+
|
1481
|
+
render_result = render_with_format(json_response, options, 'volumes')
|
1482
|
+
return 0 if render_result
|
1483
|
+
|
1484
|
+
title = "Morpheus Cluster Services: #{cluster['name']}"
|
1485
|
+
subtitles = []
|
1486
|
+
subtitles += parse_list_subtitles(options)
|
1487
|
+
print_h1 title, subtitles
|
1488
|
+
services = json_response['services']
|
1489
|
+
if services.empty?
|
1490
|
+
print yellow,"No services found.",reset,"\n"
|
1491
|
+
else
|
1492
|
+
# more stuff to show here
|
1493
|
+
rows = services.collect do |service|
|
1494
|
+
{
|
1495
|
+
id: service['id'],
|
1496
|
+
name: service['name'],
|
1497
|
+
type: service['type'],
|
1498
|
+
externalIp: service['externalIp'],
|
1499
|
+
externalPort: service['externalPort'],
|
1500
|
+
internalPort: service['internalPort'],
|
1501
|
+
status: service['status'],
|
1502
|
+
cluster: cluster['name']
|
1503
|
+
}
|
1504
|
+
end
|
1505
|
+
columns = [
|
1506
|
+
:id, :name, :type, :externalIp, :externalPort, :internalPort, :status, :cluster => lambda { |it| cluster['name'] }
|
1507
|
+
]
|
1508
|
+
print as_pretty_table(rows, columns, options)
|
1509
|
+
end
|
1510
|
+
print reset,"\n"
|
1511
|
+
return 0
|
1512
|
+
rescue RestClient::Exception => e
|
1513
|
+
print_rest_exception(e, options)
|
1514
|
+
exit 1
|
1515
|
+
end
|
1516
|
+
end
|
1517
|
+
|
1518
|
+
def remove_service(args)
|
1519
|
+
params = {}
|
1520
|
+
options = {}
|
1521
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
1522
|
+
opts.banner = subcommand_usage("[cluster] [service]")
|
1523
|
+
build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :quiet, :remote])
|
1524
|
+
opts.footer = "Delete a service within a cluster.\n" +
|
1525
|
+
"[cluster] is required. This is the name or id of an existing cluster.\n" +
|
1526
|
+
"[service] is required. This is the name or id of an existing service."
|
1527
|
+
end
|
1528
|
+
optparse.parse!(args)
|
1529
|
+
if args.count != 2
|
1530
|
+
raise_command_error "wrong number of arguments, expected 2 and got (#{args.count}) #{args}\n#{optparse}"
|
1531
|
+
end
|
1532
|
+
connect(options)
|
1533
|
+
|
1534
|
+
begin
|
1535
|
+
cluster = find_cluster_by_name_or_id(args[0])
|
1536
|
+
return 1 if cluster.nil?
|
1537
|
+
service_id = args[1]
|
1538
|
+
|
1539
|
+
if service_id.empty?
|
1540
|
+
raise_command_error "missing required service parameter"
|
1541
|
+
end
|
1542
|
+
|
1543
|
+
service = find_service_by_name_or_id(cluster['id'], service_id)
|
1544
|
+
if service.nil?
|
1545
|
+
print_red_alert "Service not found by id '#{service_id}'"
|
1546
|
+
return 1
|
1547
|
+
end
|
1548
|
+
unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to remove the cluster service '#{service['name'] || service['id']}'?", options)
|
1549
|
+
return 9, "aborted command"
|
1550
|
+
end
|
1551
|
+
|
1552
|
+
@clusters_interface.setopts(options)
|
1553
|
+
if options[:dry_run]
|
1554
|
+
print_dry_run @clusters_interface.dry.destroy_service(cluster['id'], service['id'], params)
|
1555
|
+
return
|
1556
|
+
end
|
1557
|
+
json_response = @clusters_interface.destroy_service(cluster['id'], service['id'], params)
|
1558
|
+
if options[:json]
|
1559
|
+
print JSON.pretty_generate(json_response)
|
1560
|
+
print "\n"
|
1561
|
+
elsif !options[:quiet]
|
1562
|
+
print_red_alert "Error removing service #{service['name']} from cluster #{cluster['name']}: #{json_response['msg']}" if json_response['success'] == false
|
1563
|
+
print_green_success "Service #{service['name']} is being removed from cluster #{cluster['name']}..." if json_response['success'] == true
|
1564
|
+
end
|
1565
|
+
rescue RestClient::Exception => e
|
1566
|
+
print_rest_exception(e, options)
|
1567
|
+
exit 1
|
1568
|
+
end
|
1569
|
+
end
|
1570
|
+
|
1571
|
+
def list_jobs(args)
|
1572
|
+
options = {}
|
1573
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
1574
|
+
opts.banner = subcommand_usage( "[cluster]")
|
1575
|
+
build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
|
1576
|
+
opts.footer = "List jobs for a cluster.\n" +
|
1577
|
+
"[cluster] is required. This is the name or id of an existing cluster."
|
1578
|
+
end
|
1579
|
+
|
1580
|
+
optparse.parse!(args)
|
1581
|
+
if args.count != 1
|
1582
|
+
raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
|
1583
|
+
end
|
1584
|
+
connect(options)
|
1585
|
+
begin
|
1586
|
+
cluster = find_cluster_by_name_or_id(args[0])
|
1587
|
+
return 1 if cluster.nil?
|
1588
|
+
|
1589
|
+
params = {}
|
1590
|
+
params.merge!(parse_list_options(options))
|
1591
|
+
@clusters_interface.setopts(options)
|
1592
|
+
if options[:dry_run]
|
1593
|
+
print_dry_run @clusters_interface.dry.list_jobs(cluster['id'], params)
|
1594
|
+
return
|
1595
|
+
end
|
1596
|
+
json_response = @clusters_interface.list_jobs(cluster['id'], params)
|
1597
|
+
|
1598
|
+
render_result = render_with_format(json_response, options, 'volumes')
|
1599
|
+
return 0 if render_result
|
1600
|
+
|
1601
|
+
title = "Morpheus Cluster Jobs: #{cluster['name']}"
|
1602
|
+
subtitles = []
|
1603
|
+
subtitles += parse_list_subtitles(options)
|
1604
|
+
print_h1 title, subtitles
|
1605
|
+
jobs = json_response['jobs']
|
1606
|
+
if jobs.empty?
|
1607
|
+
print yellow,"No jobs found.",reset,"\n"
|
1608
|
+
else
|
1609
|
+
# more stuff to show here
|
1610
|
+
rows = jobs.collect do |job|
|
1611
|
+
{
|
1612
|
+
id: job['id'],
|
1613
|
+
status: job['type'],
|
1614
|
+
namespace: job['namespace'],
|
1615
|
+
name: job['name'],
|
1616
|
+
lastRun: format_local_dt(job['lastRun']),
|
1617
|
+
cluster: cluster['name']
|
1618
|
+
}
|
1619
|
+
end
|
1620
|
+
columns = [
|
1621
|
+
:id, :status, :namespace, :name, :lastRun, :cluster => lambda { |it| cluster['name'] }
|
1622
|
+
]
|
1623
|
+
print as_pretty_table(rows, columns, options)
|
1624
|
+
end
|
1625
|
+
print reset,"\n"
|
1626
|
+
return 0
|
1627
|
+
rescue RestClient::Exception => e
|
1628
|
+
print_rest_exception(e, options)
|
1629
|
+
exit 1
|
1630
|
+
end
|
1631
|
+
end
|
1632
|
+
|
1633
|
+
def remove_job(args)
|
1634
|
+
params = {}
|
1635
|
+
options = {}
|
1636
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
1637
|
+
opts.banner = subcommand_usage("[cluster] [job]")
|
1638
|
+
build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :quiet, :remote])
|
1639
|
+
opts.footer = "Delete a job within a cluster.\n" +
|
1640
|
+
"[cluster] is required. This is the name or id of an existing cluster.\n" +
|
1641
|
+
"[job] is required. This is the name or id of an existing job."
|
1642
|
+
end
|
1643
|
+
optparse.parse!(args)
|
1644
|
+
if args.count != 2
|
1645
|
+
raise_command_error "wrong number of arguments, expected 2 and got (#{args.count}) #{args}\n#{optparse}"
|
1646
|
+
end
|
1647
|
+
connect(options)
|
1648
|
+
|
1649
|
+
begin
|
1650
|
+
cluster = find_cluster_by_name_or_id(args[0])
|
1651
|
+
return 1 if cluster.nil?
|
1652
|
+
job_id = args[1]
|
1653
|
+
|
1654
|
+
if job_id.empty?
|
1655
|
+
raise_command_error "missing required job parameter"
|
1656
|
+
end
|
1657
|
+
|
1658
|
+
job = find_job_by_name_or_id(cluster['id'], job_id)
|
1659
|
+
if job.nil?
|
1660
|
+
print_red_alert "Job not found by id '#{job_id}'"
|
1661
|
+
return 1
|
1662
|
+
end
|
1663
|
+
unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to remove the cluster job '#{job['name'] || job['id']}'?", options)
|
1664
|
+
return 9, "aborted command"
|
1665
|
+
end
|
1666
|
+
|
1667
|
+
@clusters_interface.setopts(options)
|
1668
|
+
if options[:dry_run]
|
1669
|
+
print_dry_run @clusters_interface.dry.destroy_job(cluster['id'], job['id'], params)
|
1670
|
+
return
|
1671
|
+
end
|
1672
|
+
json_response = @clusters_interface.destroy_job(cluster['id'], job['id'], params)
|
1673
|
+
if options[:json]
|
1674
|
+
print JSON.pretty_generate(json_response)
|
1675
|
+
print "\n"
|
1676
|
+
elsif !options[:quiet]
|
1677
|
+
print_red_alert "Error removing job #{job['name']} from cluster #{cluster['name']}: #{json_response['msg']}" if json_response['success'] == false
|
1678
|
+
print_green_success "Job #{job['name']} is being removed from cluster #{cluster['name']}..." if json_response['success'] == true
|
1679
|
+
end
|
1680
|
+
rescue RestClient::Exception => e
|
1681
|
+
print_rest_exception(e, options)
|
1682
|
+
exit 1
|
1683
|
+
end
|
1684
|
+
end
|
1685
|
+
|
1686
|
+
def list_containers(args)
|
1687
|
+
options = {}
|
1688
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
1689
|
+
opts.banner = subcommand_usage( "[cluster]")
|
1690
|
+
opts.on("--resource-level LEVEL", String, "Resource Level") do |val|
|
1691
|
+
options[:resourceLevel] = val.to_s
|
1692
|
+
end
|
1693
|
+
opts.on("--worker WORKER", String, "Worker") do |val|
|
1694
|
+
options[:worker] = val
|
1695
|
+
end
|
1696
|
+
build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
|
1697
|
+
opts.footer = "List containers for a cluster.\n" +
|
1698
|
+
"[cluster] is required. This is the name or id of an existing cluster."
|
1699
|
+
end
|
1700
|
+
|
1701
|
+
optparse.parse!(args)
|
1702
|
+
if args.count != 1
|
1703
|
+
raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
|
1704
|
+
end
|
1705
|
+
connect(options)
|
1706
|
+
begin
|
1707
|
+
cluster = find_cluster_by_name_or_id(args[0])
|
1708
|
+
return 1 if cluster.nil?
|
1709
|
+
if options[:worker]
|
1710
|
+
worker = find_host_by_name_or_id(options[:worker])
|
1711
|
+
return 1 if worker.nil?
|
1712
|
+
params['workerId'] = worker['id']
|
1713
|
+
end
|
1714
|
+
params = {}
|
1715
|
+
params.merge!(parse_list_options(options))
|
1716
|
+
params['resourceLevel'] = options[:resourceLevel] if !options[:resourceLevel].nil?
|
1717
|
+
@clusters_interface.setopts(options)
|
1718
|
+
if options[:dry_run]
|
1719
|
+
print_dry_run @clusters_interface.dry.list_containers(cluster['id'], params)
|
1720
|
+
return
|
1721
|
+
end
|
1722
|
+
json_response = @clusters_interface.list_containers(cluster['id'], params)
|
1723
|
+
|
1724
|
+
render_result = render_with_format(json_response, options, 'containers')
|
1725
|
+
return 0 if render_result
|
1726
|
+
|
1727
|
+
title = "Morpheus Cluster Containers: #{cluster['name']}"
|
1728
|
+
subtitles = []
|
1729
|
+
subtitles += parse_list_subtitles(options)
|
1730
|
+
print_h1 title, subtitles
|
1731
|
+
containers = json_response['containers']
|
1732
|
+
if containers.empty?
|
1733
|
+
print yellow,"No containers found.",reset,"\n"
|
1734
|
+
else
|
1735
|
+
# more stuff to show here
|
1736
|
+
rows = containers.collect do |it|
|
1737
|
+
{
|
1738
|
+
id: it['id'],
|
1739
|
+
status: it['status'],
|
1740
|
+
name: it['name'],
|
1741
|
+
instance: it['instance'].nil? ? '' : it['instance']['name'],
|
1742
|
+
type: it['containerType'].nil? ? '' : it['containerType']['name'],
|
1743
|
+
location: it['ip'],
|
1744
|
+
cluster: cluster['name']
|
1745
|
+
}
|
1746
|
+
end
|
1747
|
+
columns = [
|
1748
|
+
:id, :status, :name, :instance, :type, :location, :cluster => lambda { |it| cluster['name'] }
|
1749
|
+
]
|
1750
|
+
print as_pretty_table(rows, columns, options)
|
1751
|
+
end
|
1752
|
+
print reset,"\n"
|
1753
|
+
return 0
|
1754
|
+
rescue RestClient::Exception => e
|
1755
|
+
print_rest_exception(e, options)
|
1756
|
+
exit 1
|
1757
|
+
end
|
1758
|
+
end
|
1759
|
+
|
1760
|
+
def remove_container(args)
|
1761
|
+
params = {}
|
1762
|
+
options = {}
|
1763
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
1764
|
+
opts.banner = subcommand_usage("[cluster] [container]")
|
1765
|
+
opts.on( '-f', '--force', "Force Delete" ) do
|
1766
|
+
options[:force] = 'on'
|
1767
|
+
end
|
1768
|
+
build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :quiet, :remote])
|
1769
|
+
opts.footer = "Delete a container within a cluster.\n" +
|
1770
|
+
"[cluster] is required. This is the name or id of an existing cluster.\n" +
|
1771
|
+
"[container] is required. This is the name or id of an existing container."
|
1772
|
+
end
|
1773
|
+
optparse.parse!(args)
|
1774
|
+
if args.count != 2
|
1775
|
+
raise_command_error "wrong number of arguments, expected 2 and got (#{args.count}) #{args}\n#{optparse}"
|
1776
|
+
end
|
1777
|
+
connect(options)
|
1778
|
+
|
1779
|
+
begin
|
1780
|
+
cluster = find_cluster_by_name_or_id(args[0])
|
1781
|
+
return 1 if cluster.nil?
|
1782
|
+
container_id = args[1]
|
1783
|
+
|
1784
|
+
if container_id.empty?
|
1785
|
+
raise_command_error "missing required container parameter"
|
1786
|
+
end
|
1787
|
+
|
1788
|
+
container = find_container_by_name_or_id(cluster['id'], container_id)
|
1789
|
+
if container.nil?
|
1790
|
+
print_red_alert "Container not found by id '#{container_id}'"
|
1791
|
+
return 1
|
1792
|
+
end
|
1793
|
+
unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to remove the cluster container '#{container['name'] || container['id']}'?", options)
|
1794
|
+
return 9, "aborted command"
|
1795
|
+
end
|
1796
|
+
|
1797
|
+
if !options[:force].nil?
|
1798
|
+
params['force'] = options[:force]
|
1799
|
+
end
|
1800
|
+
|
1801
|
+
@clusters_interface.setopts(options)
|
1802
|
+
if options[:dry_run]
|
1803
|
+
print_dry_run @clusters_interface.dry.destroy_container(cluster['id'], container['id'], params)
|
1804
|
+
return
|
1805
|
+
end
|
1806
|
+
json_response = @clusters_interface.destroy_container(cluster['id'], container['id'], params)
|
1807
|
+
if options[:json]
|
1808
|
+
print JSON.pretty_generate(json_response)
|
1809
|
+
print "\n"
|
1810
|
+
elsif !options[:quiet]
|
1811
|
+
print_red_alert "Error removing container #{container['name']} from cluster #{cluster['name']}: #{json_response['msg']}" if json_response['success'] == false
|
1812
|
+
print_green_success "container #{container['name']} is being removed from cluster #{cluster['name']}..." if json_response['success'] == true
|
1813
|
+
end
|
1814
|
+
rescue RestClient::Exception => e
|
1815
|
+
print_rest_exception(e, options)
|
1816
|
+
exit 1
|
1817
|
+
end
|
1818
|
+
end
|
1819
|
+
|
1820
|
+
def restart_container(args)
|
1821
|
+
params = {}
|
1822
|
+
options = {}
|
1823
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
1824
|
+
opts.banner = subcommand_usage("[cluster] [container]")
|
1825
|
+
build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :quiet, :remote])
|
1826
|
+
opts.footer = "Restart a container within a cluster.\n" +
|
1827
|
+
"[cluster] is required. This is the name or id of an existing cluster.\n" +
|
1828
|
+
"[container] is required. This is the name or id of an existing container."
|
1829
|
+
end
|
1830
|
+
optparse.parse!(args)
|
1831
|
+
if args.count != 2
|
1832
|
+
raise_command_error "wrong number of arguments, expected 2 and got (#{args.count}) #{args}\n#{optparse}"
|
1833
|
+
end
|
1834
|
+
connect(options)
|
1835
|
+
|
1836
|
+
begin
|
1837
|
+
cluster = find_cluster_by_name_or_id(args[0])
|
1838
|
+
return 1 if cluster.nil?
|
1839
|
+
container_id = args[1]
|
1840
|
+
|
1841
|
+
if container_id.empty?
|
1842
|
+
raise_command_error "missing required container parameter"
|
1843
|
+
end
|
1844
|
+
|
1845
|
+
container = find_container_by_name_or_id(cluster['id'], container_id)
|
1846
|
+
if container.nil?
|
1847
|
+
print_red_alert "Container not found by id '#{container_id}'"
|
1848
|
+
return 1
|
1849
|
+
end
|
1850
|
+
|
1851
|
+
@clusters_interface.setopts(options)
|
1852
|
+
if options[:dry_run]
|
1853
|
+
print_dry_run @clusters_interface.dry.restart_container(cluster['id'], container['id'], params)
|
1854
|
+
return
|
1855
|
+
end
|
1856
|
+
json_response = @clusters_interface.restart_container(cluster['id'], container['id'], params)
|
1857
|
+
if options[:json]
|
1858
|
+
print JSON.pretty_generate(json_response)
|
1859
|
+
print "\n"
|
1860
|
+
elsif !options[:quiet]
|
1861
|
+
print_red_alert "Error restarting container #{container['name']} for cluster #{cluster['name']}: #{json_response['msg']}" if json_response['success'] == false
|
1862
|
+
print_green_success "Container #{container['name']} is restarting for cluster #{cluster['name']}..." if json_response['success'] == true
|
1863
|
+
end
|
1864
|
+
rescue RestClient::Exception => e
|
1865
|
+
print_rest_exception(e, options)
|
1866
|
+
exit 1
|
1867
|
+
end
|
1868
|
+
end
|
1869
|
+
|
1870
|
+
def _list_container_groups(args, options, resource_type)
|
1871
|
+
begin
|
1872
|
+
cluster = find_cluster_by_name_or_id(args[0])
|
1873
|
+
return 1 if cluster.nil?
|
1874
|
+
|
1875
|
+
params = {}
|
1876
|
+
params.merge!(parse_list_options(options))
|
1877
|
+
params['resourceLevel'] = options[:resourceLevel] if !options[:resourceLevel].nil?
|
1878
|
+
@clusters_interface.setopts(options)
|
1879
|
+
if options[:dry_run]
|
1880
|
+
print_dry_run @clusters_interface.dry.list_container_groups(cluster['id'], resource_type, params)
|
1881
|
+
return
|
1882
|
+
end
|
1883
|
+
json_response = @clusters_interface.list_container_groups(cluster['id'], resource_type, params)
|
1884
|
+
|
1885
|
+
render_result = render_with_format(json_response, options, 'containers')
|
1886
|
+
return 0 if render_result
|
1887
|
+
|
1888
|
+
title = "Morpheus Cluster #{resource_type.capitalize}s: #{cluster['name']}"
|
1889
|
+
subtitles = []
|
1890
|
+
subtitles += parse_list_subtitles(options)
|
1891
|
+
print_h1 title, subtitles
|
1892
|
+
container_groups = json_response["#{resource_type}s"]
|
1893
|
+
if container_groups.empty?
|
1894
|
+
print yellow,"No #{resource_type}s found.",reset,"\n"
|
1895
|
+
else
|
1896
|
+
# more stuff to show here
|
1897
|
+
rows = container_groups.collect do |it|
|
1898
|
+
stats = it['stats']
|
1899
|
+
cpu_usage_str = generate_usage_bar((it['totalCpuUsage']).to_f, 100, {max_bars: 10})
|
1900
|
+
memory_usage_str = !stats ? "" : generate_usage_bar(stats['usedMemory'], stats['maxMemory'], {max_bars: 10})
|
1901
|
+
storage_usage_str = !stats ? "" : generate_usage_bar(stats['usedStorage'], stats['maxStorage'], {max_bars: 10})
|
1902
|
+
{
|
1903
|
+
id: it['id'],
|
1904
|
+
status: it['status'],
|
1905
|
+
name: it['name'],
|
1906
|
+
cpu: cpu_usage_str + cyan,
|
1907
|
+
memory: memory_usage_str + cyan,
|
1908
|
+
storage: storage_usage_str + cyan
|
1909
|
+
}
|
1910
|
+
end
|
1911
|
+
columns = [
|
1912
|
+
:id, :status, :name, :cpu, :memory, :storage
|
1913
|
+
]
|
1914
|
+
print as_pretty_table(rows, columns, options)
|
1915
|
+
end
|
1916
|
+
print reset,"\n"
|
1917
|
+
return 0
|
1918
|
+
rescue RestClient::Exception => e
|
1919
|
+
print_rest_exception(e, options)
|
1920
|
+
exit 1
|
1921
|
+
end
|
1922
|
+
end
|
1923
|
+
|
1924
|
+
def _remove_container_group(args, options, resource_type)
|
1925
|
+
begin
|
1926
|
+
cluster = find_cluster_by_name_or_id(args[0])
|
1927
|
+
return 1 if cluster.nil?
|
1928
|
+
container_group_id = args[1]
|
1929
|
+
|
1930
|
+
if container_group_id.empty?
|
1931
|
+
raise_command_error "missing required container parameter"
|
1932
|
+
end
|
1933
|
+
|
1934
|
+
container_group = find_container_group_by_name_or_id(cluster['id'], resource_type, container_group_id)
|
1935
|
+
if container_group.nil?
|
1936
|
+
print_red_alert "#{resource_type.capitalize} not found by id '#{container_group_id}'"
|
1937
|
+
return 1
|
1938
|
+
end
|
1939
|
+
unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to remove the cluster #{resource_type} '#{container_group['name'] || container_group['id']}'?", options)
|
1940
|
+
return 9, "aborted command"
|
1941
|
+
end
|
1942
|
+
|
1943
|
+
params = {}
|
1944
|
+
params.merge!(parse_list_options(options))
|
1945
|
+
|
1946
|
+
if !options[:force].nil?
|
1947
|
+
params['force'] = options[:force]
|
1948
|
+
end
|
1949
|
+
|
1950
|
+
@clusters_interface.setopts(options)
|
1951
|
+
if options[:dry_run]
|
1952
|
+
print_dry_run @clusters_interface.dry.destroy_container_group(cluster['id'], container_group['id'], resource_type, params)
|
1953
|
+
return
|
1954
|
+
end
|
1955
|
+
json_response = @clusters_interface.destroy_container_group(cluster['id'], container_group['id'], resource_type, params)
|
1956
|
+
if options[:json]
|
1957
|
+
print JSON.pretty_generate(json_response)
|
1958
|
+
print "\n"
|
1959
|
+
elsif !options[:quiet]
|
1960
|
+
print_red_alert "Error removing #{resource_type} #{container_group['name']} from cluster #{cluster['name']}: #{json_response['msg']}" if json_response['success'] == false
|
1961
|
+
print_green_success "#{resource_type.capitalize} #{container_group['name']} is being removed from cluster #{cluster['name']}..." if json_response['success'] == true
|
1962
|
+
end
|
1963
|
+
rescue RestClient::Exception => e
|
1964
|
+
print_rest_exception(e, options)
|
1965
|
+
exit 1
|
1966
|
+
end
|
1967
|
+
end
|
1968
|
+
|
1969
|
+
def _restart_container_group(args, options, resource_type)
|
1970
|
+
begin
|
1971
|
+
cluster = find_cluster_by_name_or_id(args[0])
|
1972
|
+
return 1 if cluster.nil?
|
1973
|
+
container_group_id = args[1]
|
1974
|
+
|
1975
|
+
if container_group_id.empty?
|
1976
|
+
raise_command_error "missing required container parameter"
|
1977
|
+
end
|
1978
|
+
|
1979
|
+
container_group = find_container_group_by_name_or_id(cluster['id'], resource_type, container_group_id)
|
1980
|
+
if container_group.nil?
|
1981
|
+
print_red_alert "#{resource_type.capitalize} not found by id '#{container_group_id}'"
|
1982
|
+
return 1
|
1983
|
+
end
|
1984
|
+
|
1985
|
+
params = {}
|
1986
|
+
params.merge!(parse_list_options(options))
|
1987
|
+
|
1988
|
+
if !options[:force].nil?
|
1989
|
+
params['force'] = options[:force]
|
1990
|
+
end
|
1991
|
+
|
1992
|
+
@clusters_interface.setopts(options)
|
1993
|
+
if options[:dry_run]
|
1994
|
+
print_dry_run @clusters_interface.dry.restart_container_group(cluster['id'], container_group['id'], resource_type, params)
|
1995
|
+
return
|
1996
|
+
end
|
1997
|
+
json_response = @clusters_interface.restart_container_group(cluster['id'], container_group['id'], resource_type, params)
|
1998
|
+
if options[:json]
|
1999
|
+
print JSON.pretty_generate(json_response)
|
2000
|
+
print "\n"
|
2001
|
+
elsif !options[:quiet]
|
2002
|
+
print_red_alert "Error restarting #{resource_type} #{container_group['name']} from cluster #{cluster['name']}: #{json_response['msg']}" if json_response['success'] == false
|
2003
|
+
print_green_success "#{resource_type.capitalize} #{container_group['name']} is being restarted for cluster #{cluster['name']}..." if json_response['success'] == true
|
2004
|
+
end
|
2005
|
+
rescue RestClient::Exception => e
|
2006
|
+
print_rest_exception(e, options)
|
2007
|
+
exit 1
|
2008
|
+
end
|
2009
|
+
end
|
2010
|
+
|
2011
|
+
def list_deployments(args)
|
2012
|
+
resource_type = 'deployment'
|
2013
|
+
options = {}
|
2014
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
2015
|
+
opts.banner = subcommand_usage( "[cluster]")
|
2016
|
+
opts.on("--resource-level LEVEL", String, "Resource Level") do |val|
|
2017
|
+
options[:resourceLevel] = val.to_s
|
2018
|
+
end
|
2019
|
+
build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
|
2020
|
+
opts.footer = "List #{resource_type}s for a cluster.\n" +
|
2021
|
+
"[cluster] is required. This is the name or id of an existing cluster."
|
2022
|
+
end
|
2023
|
+
optparse.parse!(args)
|
2024
|
+
if args.count != 1
|
2025
|
+
raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
|
2026
|
+
end
|
2027
|
+
connect(options)
|
2028
|
+
_list_container_groups(args, options,resource_type)
|
2029
|
+
end
|
2030
|
+
|
2031
|
+
def remove_deployment(args)
|
2032
|
+
resource_type = 'deployment'
|
2033
|
+
options = {}
|
2034
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
2035
|
+
opts.banner = subcommand_usage("[cluster] [#{resource_type}]")
|
2036
|
+
opts.on( '-f', '--force', "Force Delete" ) do
|
2037
|
+
options[:force] = 'on'
|
2038
|
+
end
|
2039
|
+
build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :quiet, :remote])
|
2040
|
+
opts.footer = "Delete a #{resource_type} within a cluster.\n" +
|
2041
|
+
"[cluster] is required. This is the name or id of an existing cluster.\n" +
|
2042
|
+
"[#{resource_type}] is required. This is the name or id of an existing #{resource_type}."
|
2043
|
+
end
|
2044
|
+
optparse.parse!(args)
|
2045
|
+
if args.count != 2
|
2046
|
+
raise_command_error "wrong number of arguments, expected 2 and got (#{args.count}) #{args}\n#{optparse}"
|
2047
|
+
end
|
2048
|
+
connect(options)
|
2049
|
+
_remove_container_group(args, options, resource_type)
|
2050
|
+
end
|
2051
|
+
|
2052
|
+
def restart_deployment(args)
|
2053
|
+
resource_type = 'deployment'
|
2054
|
+
options = {}
|
2055
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
2056
|
+
opts.banner = subcommand_usage("[cluster] [#{resource_type}]")
|
2057
|
+
build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :quiet, :remote])
|
2058
|
+
opts.footer = "Restart a #{resource_type} within a cluster.\n" +
|
2059
|
+
"[cluster] is required. This is the name or id of an existing cluster.\n" +
|
2060
|
+
"[#{resource_type}] is required. This is the name or id of an existing #{resource_type}."
|
2061
|
+
end
|
2062
|
+
optparse.parse!(args)
|
2063
|
+
if args.count != 2
|
2064
|
+
raise_command_error "wrong number of arguments, expected 2 and got (#{args.count}) #{args}\n#{optparse}"
|
2065
|
+
end
|
2066
|
+
connect(options)
|
2067
|
+
_restart_container_group(args, options, resource_type)
|
2068
|
+
end
|
2069
|
+
|
2070
|
+
def list_stateful_sets(args)
|
2071
|
+
resource_type = 'statefulset'
|
2072
|
+
options = {}
|
2073
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
2074
|
+
opts.banner = subcommand_usage( "[cluster]")
|
2075
|
+
opts.on("--resource-level LEVEL", String, "Resource Level") do |val|
|
2076
|
+
options[:resourceLevel] = val.to_s
|
2077
|
+
end
|
2078
|
+
build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
|
2079
|
+
opts.footer = "List #{resource_type}s for a cluster.\n" +
|
2080
|
+
"[cluster] is required. This is the name or id of an existing cluster."
|
2081
|
+
end
|
2082
|
+
optparse.parse!(args)
|
2083
|
+
if args.count != 1
|
2084
|
+
raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
|
2085
|
+
end
|
2086
|
+
connect(options)
|
2087
|
+
_list_container_groups(args, options, resource_type)
|
2088
|
+
end
|
2089
|
+
|
2090
|
+
def remove_stateful_set(args)
|
2091
|
+
resource_type = 'statefulset'
|
2092
|
+
options = {}
|
2093
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
2094
|
+
opts.banner = subcommand_usage("[cluster] [#{resource_type}]")
|
2095
|
+
opts.on( '-f', '--force', "Force Delete" ) do
|
2096
|
+
options[:force] = 'on'
|
2097
|
+
end
|
2098
|
+
build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :quiet, :remote])
|
2099
|
+
opts.footer = "Delete a #{resource_type} within a cluster.\n" +
|
2100
|
+
"[cluster] is required. This is the name or id of an existing cluster.\n" +
|
2101
|
+
"[#{resource_type}] is required. This is the name or id of an existing #{resource_type}."
|
2102
|
+
end
|
2103
|
+
optparse.parse!(args)
|
2104
|
+
if args.count != 2
|
2105
|
+
raise_command_error "wrong number of arguments, expected 2 and got (#{args.count}) #{args}\n#{optparse}"
|
2106
|
+
end
|
2107
|
+
connect(options)
|
2108
|
+
_remove_container_group(args, options, resource_type)
|
2109
|
+
end
|
2110
|
+
|
2111
|
+
def restart_stateful_set(args)
|
2112
|
+
resource_type = 'statefulset'
|
2113
|
+
options = {}
|
2114
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
2115
|
+
opts.banner = subcommand_usage("[cluster] [#{resource_type}]")
|
2116
|
+
build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :quiet, :remote])
|
2117
|
+
opts.footer = "Restart a #{resource_type} within a cluster.\n" +
|
2118
|
+
"[cluster] is required. This is the name or id of an existing cluster.\n" +
|
2119
|
+
"[#{resource_type}] is required. This is the name or id of an existing #{resource_type}."
|
2120
|
+
end
|
2121
|
+
optparse.parse!(args)
|
2122
|
+
if args.count != 2
|
2123
|
+
raise_command_error "wrong number of arguments, expected 2 and got (#{args.count}) #{args}\n#{optparse}"
|
2124
|
+
end
|
2125
|
+
connect(options)
|
2126
|
+
_restart_container_group(args, options, resource_type)
|
2127
|
+
end
|
2128
|
+
|
2129
|
+
def list_pods(args)
|
2130
|
+
resource_type = 'pod'
|
2131
|
+
options = {}
|
2132
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
2133
|
+
opts.banner = subcommand_usage( "[cluster]")
|
2134
|
+
opts.on("--resource-level LEVEL", String, "Resource Level") do |val|
|
2135
|
+
options[:resourceLevel] = val.to_s
|
2136
|
+
end
|
2137
|
+
build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
|
2138
|
+
opts.footer = "List #{resource_type}s for a cluster.\n" +
|
2139
|
+
"[cluster] is required. This is the name or id of an existing cluster."
|
2140
|
+
end
|
2141
|
+
optparse.parse!(args)
|
2142
|
+
if args.count != 1
|
2143
|
+
raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
|
2144
|
+
end
|
2145
|
+
connect(options)
|
2146
|
+
_list_container_groups(args, options, resource_type)
|
2147
|
+
end
|
2148
|
+
|
2149
|
+
def remove_pod(args)
|
2150
|
+
resource_type = 'pod'
|
2151
|
+
options = {}
|
2152
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
2153
|
+
opts.banner = subcommand_usage("[cluster] [#{resource_type}]")
|
2154
|
+
opts.on( '-f', '--force', "Force Delete" ) do
|
2155
|
+
options[:force] = 'on'
|
2156
|
+
end
|
2157
|
+
build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :quiet, :remote])
|
2158
|
+
opts.footer = "Delete a #{resource_type} within a cluster.\n" +
|
2159
|
+
"[cluster] is required. This is the name or id of an existing cluster.\n" +
|
2160
|
+
"[#{resource_type}] is required. This is the name or id of an existing #{resource_type}."
|
2161
|
+
end
|
2162
|
+
optparse.parse!(args)
|
2163
|
+
if args.count != 2
|
2164
|
+
raise_command_error "wrong number of arguments, expected 2 and got (#{args.count}) #{args}\n#{optparse}"
|
2165
|
+
end
|
2166
|
+
connect(options)
|
2167
|
+
_remove_container_group(args, options, resource_type)
|
2168
|
+
end
|
2169
|
+
|
2170
|
+
def restart_pod(args)
|
2171
|
+
resource_type = 'pod'
|
2172
|
+
options = {}
|
2173
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
2174
|
+
opts.banner = subcommand_usage("[cluster] [#{resource_type}]")
|
2175
|
+
build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :quiet, :remote])
|
2176
|
+
opts.footer = "Restart a #{resource_type} within a cluster.\n" +
|
2177
|
+
"[cluster] is required. This is the name or id of an existing cluster.\n" +
|
2178
|
+
"[#{resource_type}] is required. This is the name or id of an existing #{resource_type}."
|
2179
|
+
end
|
2180
|
+
optparse.parse!(args)
|
2181
|
+
if args.count != 2
|
2182
|
+
raise_command_error "wrong number of arguments, expected 2 and got (#{args.count}) #{args}\n#{optparse}"
|
2183
|
+
end
|
2184
|
+
connect(options)
|
2185
|
+
_restart_container_group(args, options, resource_type)
|
2186
|
+
end
|
2187
|
+
|
2188
|
+
def add_namespace(args)
|
2189
|
+
options = {}
|
2190
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
2191
|
+
opts.banner = subcommand_usage( "[cluster] [name] [options]")
|
2192
|
+
opts.on("--name NAME", String, "Name of the new namespace") do |val|
|
2193
|
+
options[:name] = val.to_s
|
2194
|
+
end
|
2195
|
+
opts.on("--description [TEXT]", String, "Description") do |val|
|
2196
|
+
options[:description] = val.to_s
|
2197
|
+
end
|
2198
|
+
opts.on('--active [on|off]', String, "Enable namespace") do |val|
|
2199
|
+
options[:active] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == ''
|
2200
|
+
end
|
2201
|
+
add_perms_options(opts, options)
|
2202
|
+
build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote])
|
2203
|
+
opts.footer = "Create a cluster namespace.\n" +
|
2204
|
+
"[cluster] is required. This is the name or id of an existing cluster.\n" +
|
2205
|
+
"[name] is required. This is the name of the new namespace."
|
2206
|
+
end
|
2207
|
+
|
2208
|
+
optparse.parse!(args)
|
2209
|
+
if args.count < 1 || args.count > 3
|
2210
|
+
raise_command_error "wrong number of arguments, expected 1 to 3 and got (#{args.count}) #{args}\n#{optparse}"
|
2211
|
+
end
|
2212
|
+
connect(options)
|
2213
|
+
|
2214
|
+
begin
|
2215
|
+
cluster = find_cluster_by_name_or_id(args[0])
|
2216
|
+
return 1 if cluster.nil?
|
2217
|
+
payload = nil
|
2218
|
+
if options[:payload]
|
2219
|
+
payload = options[:payload]
|
2220
|
+
# support -O OPTION switch on top of --payload
|
2221
|
+
if options[:options]
|
2222
|
+
payload['namespace'] ||= {}
|
2223
|
+
payload['namespace'].deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) })
|
2224
|
+
end
|
2225
|
+
else
|
2226
|
+
namespace_payload = {'name' => options[:name] || (args.length > 1 ? args[1] : nil) || Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'description' => 'Namespace Name', 'required' => true}], options[:options], @api_client)['name']}
|
2227
|
+
namespace_payload.deep_merge!(prompt_update_namespace(options).reject {|k,v| k.is_a?(Symbol)})
|
2228
|
+
payload = {"namespace" => namespace_payload}
|
2229
|
+
end
|
2230
|
+
|
2231
|
+
@clusters_interface.setopts(options)
|
2232
|
+
if options[:dry_run]
|
2233
|
+
print_dry_run @clusters_interface.dry.create_namespace(cluster['id'], payload)
|
2234
|
+
return
|
2235
|
+
end
|
2236
|
+
json_response = @clusters_interface.create_namespace(cluster['id'], payload)
|
2237
|
+
if options[:json]
|
2238
|
+
puts as_json(json_response, options)
|
2239
|
+
elsif !options[:quiet]
|
2240
|
+
namespace = json_response['namespace']
|
2241
|
+
print_green_success "Added namespace #{namespace['name']}"
|
2242
|
+
get_args = [cluster["id"], namespace["id"]] + (options[:remote] ? ["-r",options[:remote]] : [])
|
2243
|
+
get_namespace(get_args)
|
2244
|
+
end
|
2245
|
+
return 0
|
2246
|
+
rescue RestClient::Exception => e
|
2247
|
+
print_rest_exception(e, options)
|
2248
|
+
exit 1
|
2249
|
+
end
|
2250
|
+
end
|
2251
|
+
|
2252
|
+
def list_namespaces(args)
|
2253
|
+
options = {}
|
2254
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
2255
|
+
opts.banner = subcommand_usage( "[cluster]")
|
2256
|
+
build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
|
2257
|
+
opts.footer = "List namespaces for a cluster.\n" +
|
2258
|
+
"[cluster] is required. This is the name or id of an existing cluster."
|
2259
|
+
end
|
2260
|
+
|
2261
|
+
optparse.parse!(args)
|
2262
|
+
if args.count != 1
|
2263
|
+
raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
|
2264
|
+
end
|
2265
|
+
connect(options)
|
2266
|
+
begin
|
2267
|
+
cluster = find_cluster_by_name_or_id(args[0])
|
2268
|
+
return 1 if cluster.nil?
|
2269
|
+
|
2270
|
+
params = {}
|
2271
|
+
params.merge!(parse_list_options(options))
|
2272
|
+
@clusters_interface.setopts(options)
|
2273
|
+
if options[:dry_run]
|
2274
|
+
print_dry_run @clusters_interface.dry.list_namespaces(cluster['id'], params)
|
2275
|
+
return
|
2276
|
+
end
|
2277
|
+
json_response = @clusters_interface.list_namespaces(cluster['id'], params)
|
2278
|
+
|
2279
|
+
render_result = render_with_format(json_response, options, 'namespaces')
|
2280
|
+
return 0 if render_result
|
2281
|
+
|
2282
|
+
title = "Morpheus Cluster Namespaces: #{cluster['name']}"
|
2283
|
+
subtitles = []
|
2284
|
+
subtitles += parse_list_subtitles(options)
|
2285
|
+
print_h1 title, subtitles
|
2286
|
+
namespaces = json_response['namespaces']
|
2287
|
+
if namespaces.empty?
|
2288
|
+
print yellow,"No namespaces found.",reset,"\n"
|
2289
|
+
else
|
2290
|
+
# more stuff to show here
|
2291
|
+
rows = namespaces.collect do |ns|
|
2292
|
+
{
|
2293
|
+
id: ns['id'],
|
2294
|
+
name: ns['name'],
|
2295
|
+
description: ns['description'],
|
2296
|
+
status: ns['status'],
|
2297
|
+
active: format_boolean(ns['active']),
|
2298
|
+
cluster: cluster['name']
|
2299
|
+
}
|
2300
|
+
end
|
2301
|
+
columns = [
|
2302
|
+
:id, :name, :description, :status, :active #, :cluster => lambda { |it| cluster['name'] }
|
2303
|
+
]
|
2304
|
+
print as_pretty_table(rows, columns, options)
|
2305
|
+
end
|
2306
|
+
print reset,"\n"
|
2307
|
+
return 0
|
2308
|
+
rescue RestClient::Exception => e
|
2309
|
+
print_rest_exception(e, options)
|
2310
|
+
exit 1
|
2311
|
+
end
|
2312
|
+
end
|
2313
|
+
|
2314
|
+
def get_namespace(args)
|
2315
|
+
options = {}
|
2316
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
2317
|
+
opts.banner = subcommand_usage( "[cluster] [namespace]")
|
2318
|
+
opts.on( nil, '--permissions', "Display permissions" ) do
|
2319
|
+
options[:show_perms] = true
|
2320
|
+
end
|
2321
|
+
build_common_options(opts, options, [:query, :json, :yaml, :csv, :fields, :dry_run, :remote])
|
2322
|
+
opts.footer = "Get details about a cluster namespace.\n" +
|
2323
|
+
"[cluster] is required. This is the name or id of an existing cluster.\n" +
|
2324
|
+
"[namespace] is required. This is the name or id of an existing namespace."
|
2325
|
+
end
|
2326
|
+
optparse.parse!(args)
|
2327
|
+
if args.count != 2
|
2328
|
+
raise_command_error "wrong number of arguments, expected 2 and got (#{args.count}) #{args}\n#{optparse}"
|
2329
|
+
end
|
2330
|
+
connect(options)
|
2331
|
+
begin
|
2332
|
+
cluster = find_cluster_by_name_or_id(args[0])
|
2333
|
+
return 1 if cluster.nil?
|
2334
|
+
# this finds the namespace in the cluster api response, then fetches it by ID
|
2335
|
+
namespace = find_namespace_by_name_or_id(cluster['id'], args[1])
|
2336
|
+
if namespace.nil?
|
2337
|
+
print_red_alert "Namespace not found for '#{args[1]}'"
|
2338
|
+
exit 1
|
2339
|
+
end
|
2340
|
+
params = {}
|
2341
|
+
params.merge!(parse_list_options(options))
|
2342
|
+
@clusters_interface.setopts(options)
|
2343
|
+
if options[:dry_run]
|
2344
|
+
print_dry_run @clusters_interface.dry.get_namespace(cluster['id'], namespace['id'], params)
|
2345
|
+
return
|
2346
|
+
end
|
2347
|
+
json_response = @clusters_interface.get_namespace(cluster['id'], namespace['id'], params)
|
2348
|
+
|
2349
|
+
render_result = render_with_format(json_response, options, 'namespace')
|
2350
|
+
return 0 if render_result
|
2351
|
+
|
2352
|
+
print_h1 "Morpheus Cluster Namespace"
|
2353
|
+
print cyan
|
2354
|
+
description_cols = {
|
2355
|
+
"ID" => 'id',
|
2356
|
+
"Name" => 'name',
|
2357
|
+
"Description" => 'description',
|
2358
|
+
"Cluster" => lambda { |it| cluster['name'] },
|
2359
|
+
"Status" => 'status',
|
2360
|
+
"Active" => lambda {|it| format_boolean it['active'] }
|
2361
|
+
# more stuff to show here
|
2362
|
+
}
|
2363
|
+
print_description_list(description_cols, namespace)
|
2364
|
+
print reset,"\n"
|
2365
|
+
|
2366
|
+
if options[:show_perms]
|
2367
|
+
permissions = cluster['permissions']
|
2368
|
+
print_permissions(permissions)
|
2369
|
+
end
|
2370
|
+
|
2371
|
+
return 0
|
2372
|
+
rescue RestClient::Exception => e
|
2373
|
+
print_rest_exception(e, options)
|
2374
|
+
exit 1
|
2375
|
+
end
|
2376
|
+
end
|
2377
|
+
|
2378
|
+
def update_namespace(args)
|
2379
|
+
options = {}
|
2380
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
2381
|
+
opts.banner = subcommand_usage( "[cluster] [namespace] [options]")
|
2382
|
+
opts.on("--description [TEXT]", String, "Description") do |val|
|
2383
|
+
options[:description] = val.to_s
|
2384
|
+
end
|
2385
|
+
opts.on('--active [on|off]', String, "Enable namespace") do |val|
|
2386
|
+
options[:active] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == ''
|
2387
|
+
end
|
2388
|
+
add_perms_options(opts, options)
|
2389
|
+
build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote])
|
2390
|
+
opts.footer = "Update a cluster namespace.\n" +
|
2391
|
+
"[cluster] is required. This is the name or id of an existing cluster.\n" +
|
2392
|
+
"[namespace] is required. This is the name or id of an existing namespace."
|
2393
|
+
end
|
2394
|
+
|
2395
|
+
optparse.parse!(args)
|
2396
|
+
if args.count != 2
|
2397
|
+
raise_command_error "wrong number of arguments, expected 2 and got (#{args.count}) #{args}\n#{optparse}"
|
2398
|
+
end
|
2399
|
+
connect(options)
|
2400
|
+
|
2401
|
+
begin
|
2402
|
+
cluster = find_cluster_by_name_or_id(args[0])
|
2403
|
+
return 1 if cluster.nil?
|
2404
|
+
namespace = find_namespace_by_name_or_id(cluster['id'], args[1])
|
2405
|
+
if namespace.nil?
|
2406
|
+
print_red_alert "Namespace not found by '#{args[1]}'"
|
2407
|
+
exit 1
|
2408
|
+
end
|
2409
|
+
payload = nil
|
2410
|
+
if options[:payload]
|
2411
|
+
payload = options[:payload]
|
2412
|
+
# support -O OPTION switch on top of everything
|
2413
|
+
if options[:options]
|
2414
|
+
payload.deep_merge!({'namespace' => options[:options].reject {|k,v| k.is_a?(Symbol) }})
|
2415
|
+
end
|
2416
|
+
else
|
2417
|
+
payload = {'namespace' => prompt_update_namespace(options)}
|
2418
|
+
|
2419
|
+
# support -O OPTION switch on top of everything
|
2420
|
+
if options[:options]
|
2421
|
+
payload.deep_merge!({'namespace' => options[:options].reject {|k,v| k.is_a?(Symbol) }})
|
2422
|
+
end
|
2423
|
+
|
2424
|
+
if payload['namespace'].nil? || payload['namespace'].empty?
|
2425
|
+
raise_command_error "Specify at least one option to update.\n#{optparse}"
|
2426
|
+
end
|
2427
|
+
end
|
2428
|
+
|
2429
|
+
@clusters_interface.setopts(options)
|
2430
|
+
if options[:dry_run]
|
2431
|
+
print_dry_run @clusters_interface.dry.update_namespace(cluster['id'], namespace['id'], payload)
|
2432
|
+
return
|
2433
|
+
end
|
2434
|
+
json_response = @clusters_interface.update_namespace(cluster['id'], namespace['id'], payload)
|
2435
|
+
if options[:json]
|
2436
|
+
puts as_json(json_response)
|
2437
|
+
elsif !options[:quiet]
|
2438
|
+
namespace = json_response['namespace']
|
2439
|
+
print_green_success "Updated namespace #{namespace['name']}"
|
2440
|
+
get_args = [cluster["id"], namespace["id"]] + (options[:remote] ? ["-r",options[:remote]] : [])
|
2441
|
+
get_namespace(get_args)
|
2442
|
+
end
|
2443
|
+
return 0
|
2444
|
+
rescue RestClient::Exception => e
|
2445
|
+
print_rest_exception(e, options)
|
2446
|
+
exit 1
|
2447
|
+
end
|
2448
|
+
end
|
2449
|
+
|
2450
|
+
def remove_namespace(args)
|
2451
|
+
options = {}
|
2452
|
+
query_params = {}
|
2453
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
2454
|
+
opts.banner = subcommand_usage("[cluster] [namespace]")
|
2455
|
+
opts.on( '-f', '--force', "Force Delete" ) do
|
2456
|
+
query_params[:force] = 'on'
|
2457
|
+
end
|
2458
|
+
build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :quiet, :remote])
|
2459
|
+
opts.footer = "Delete a namespace within a cluster."
|
2460
|
+
end
|
2461
|
+
optparse.parse!(args)
|
2462
|
+
if args.count != 2
|
2463
|
+
raise_command_error "wrong number of arguments, expected 2 and got (#{args.count}) #{args}\n#{optparse}"
|
2464
|
+
end
|
2465
|
+
connect(options)
|
2466
|
+
|
2467
|
+
begin
|
2468
|
+
cluster = find_cluster_by_name_or_id(args[0])
|
2469
|
+
return 1 if cluster.nil?
|
2470
|
+
namespace = find_namespace_by_name_or_id(cluster['id'], args[1])
|
2471
|
+
if namespace.nil?
|
2472
|
+
print_red_alert "Namespace not found by '#{args[1]}'"
|
2473
|
+
exit 1
|
2474
|
+
end
|
2475
|
+
unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to remove the cluster namespace '#{namespace['name']}'?", options)
|
2476
|
+
return 9, "aborted command"
|
2477
|
+
end
|
2478
|
+
@clusters_interface.setopts(options)
|
2479
|
+
if options[:dry_run]
|
2480
|
+
print_dry_run @clusters_interface.dry.destroy_namespace(cluster['id'], namespace['id'], query_params)
|
2481
|
+
return
|
2482
|
+
end
|
2483
|
+
json_response = @clusters_interface.destroy_namespace(cluster['id'], namespace['id'], query_params)
|
2484
|
+
if options[:json]
|
2485
|
+
print JSON.pretty_generate(json_response)
|
2486
|
+
print "\n"
|
2487
|
+
elsif !options[:quiet]
|
2488
|
+
print_green_success "Removed cluster namespace #{namespace['name']}"
|
2489
|
+
#list([])
|
2490
|
+
end
|
2491
|
+
rescue RestClient::Exception => e
|
2492
|
+
print_rest_exception(e, options)
|
2493
|
+
exit 1
|
2494
|
+
end
|
2495
|
+
end
|
2496
|
+
|
2497
|
+
def api_config(args)
|
2498
|
+
options = {}
|
2499
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
2500
|
+
opts.banner = subcommand_usage("[cluster]")
|
2501
|
+
build_common_options(opts, options, [:query, :json, :yaml, :csv, :fields, :dry_run, :remote])
|
2502
|
+
opts.footer = "Display API service settings for a cluster."
|
2503
|
+
end
|
2504
|
+
optparse.parse!(args)
|
2505
|
+
if args.count != 1
|
2506
|
+
raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args}\n#{optparse}"
|
2507
|
+
end
|
2508
|
+
connect(options)
|
2509
|
+
begin
|
2510
|
+
cluster = find_cluster_by_name_or_id(args[0])
|
2511
|
+
return 1 if cluster.nil?
|
2512
|
+
params = {}
|
2513
|
+
params.merge!(parse_list_options(options))
|
2514
|
+
@clusters_interface.setopts(options)
|
2515
|
+
if options[:dry_run]
|
2516
|
+
print_dry_run @clusters_interface.dry.api_config(cluster['id'], params)
|
2517
|
+
return
|
2518
|
+
end
|
2519
|
+
json_response = @clusters_interface.api_config(cluster['id'], params)
|
2520
|
+
|
2521
|
+
render_result = render_with_format(json_response, options)
|
2522
|
+
return 0 if render_result
|
2523
|
+
|
2524
|
+
title = "Cluster API Config: #{cluster['name']}"
|
2525
|
+
subtitles = []
|
2526
|
+
subtitles += parse_list_subtitles(options)
|
2527
|
+
print_h1 title, subtitles, options
|
2528
|
+
|
2529
|
+
service_config = json_response
|
2530
|
+
print cyan
|
2531
|
+
description_cols = {
|
2532
|
+
"Url" => 'serviceUrl',
|
2533
|
+
"Username" => 'serviceUsername',
|
2534
|
+
#"Password" => 'servicePassword',
|
2535
|
+
"Token" => 'serviceToken',
|
2536
|
+
"Access" => 'serviceAccess',
|
2537
|
+
"Cert" => 'serviceCert',
|
2538
|
+
#"Config" => 'serviceConfig',
|
2539
|
+
"Version" => 'serviceVersion',
|
2540
|
+
}
|
2541
|
+
print_description_list(description_cols, service_config)
|
2542
|
+
print reset,"\n"
|
2543
|
+
return 0
|
2544
|
+
|
2545
|
+
rescue RestClient::Exception => e
|
2546
|
+
print_rest_exception(e, options)
|
2547
|
+
exit 1
|
2548
|
+
end
|
2549
|
+
end
|
2550
|
+
|
2551
|
+
def view_kube_config(args)
|
2552
|
+
options = {}
|
2553
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
2554
|
+
opts.banner = subcommand_usage("[cluster]")
|
2555
|
+
build_common_options(opts, options, [:query, :json, :yaml, :csv, :fields, :dry_run, :remote])
|
2556
|
+
opts.footer = "Display Kubernetes config for a cluster."
|
2557
|
+
end
|
2558
|
+
optparse.parse!(args)
|
2559
|
+
if args.count != 1
|
2560
|
+
raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args}\n#{optparse}"
|
2561
|
+
end
|
2562
|
+
connect(options)
|
2563
|
+
begin
|
2564
|
+
cluster = find_cluster_by_name_or_id(args[0])
|
2565
|
+
return 1 if cluster.nil?
|
2566
|
+
params = {}
|
2567
|
+
params.merge!(parse_list_options(options))
|
2568
|
+
@clusters_interface.setopts(options)
|
2569
|
+
if options[:dry_run]
|
2570
|
+
print_dry_run @clusters_interface.dry.api_config(cluster['id'], params)
|
2571
|
+
return
|
2572
|
+
end
|
2573
|
+
json_response = @clusters_interface.api_config(cluster['id'], params)
|
2574
|
+
|
2575
|
+
render_result = render_with_format(json_response, options)
|
2576
|
+
return 0 if render_result
|
2577
|
+
|
2578
|
+
title = "Cluster Kube Config: #{cluster['name']}"
|
2579
|
+
subtitles = []
|
2580
|
+
subtitles += parse_list_subtitles(options)
|
2581
|
+
print_h1 title, subtitles, options
|
2582
|
+
|
2583
|
+
service_config = json_response
|
2584
|
+
service_access = service_config['serviceAccess']
|
2585
|
+
if service_access.to_s.empty?
|
2586
|
+
print yellow,"No kube config found.",reset,"\n\n"
|
2587
|
+
return 1
|
2588
|
+
else
|
2589
|
+
print cyan,service_access,reset,"\n\n"
|
2590
|
+
return 0
|
2591
|
+
end
|
2592
|
+
|
2593
|
+
rescue RestClient::Exception => e
|
2594
|
+
print_rest_exception(e, options)
|
2595
|
+
exit 1
|
2596
|
+
end
|
2597
|
+
end
|
2598
|
+
|
2599
|
+
def view_api_token(args)
|
2600
|
+
print_token_only = false
|
2601
|
+
options = {}
|
2602
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
2603
|
+
opts.banner = subcommand_usage("[cluster]")
|
2604
|
+
build_common_options(opts, options, [:dry_run, :remote])
|
2605
|
+
opts.on('-t','--token-only', "Print the api token only") do
|
2606
|
+
print_token_only = true
|
2607
|
+
end
|
2608
|
+
opts.footer = "Display api token for a cluster."
|
2609
|
+
end
|
2610
|
+
optparse.parse!(args)
|
2611
|
+
if args.count != 1
|
2612
|
+
raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args}\n#{optparse}"
|
2613
|
+
end
|
2614
|
+
connect(options)
|
2615
|
+
begin
|
2616
|
+
cluster = find_cluster_by_name_or_id(args[0])
|
2617
|
+
return 1 if cluster.nil?
|
2618
|
+
params = {}
|
2619
|
+
params.merge!(parse_list_options(options))
|
2620
|
+
@clusters_interface.setopts(options)
|
2621
|
+
if options[:dry_run]
|
2622
|
+
print_dry_run @clusters_interface.dry.api_config(cluster['id'], params)
|
2623
|
+
return
|
2624
|
+
end
|
2625
|
+
json_response = @clusters_interface.api_config(cluster['id'], params)
|
2626
|
+
|
2627
|
+
render_result = render_with_format(json_response, options)
|
2628
|
+
return 0 if render_result
|
2629
|
+
|
2630
|
+
service_config = json_response
|
2631
|
+
service_token = service_config['serviceToken']
|
2632
|
+
|
2633
|
+
if print_token_only
|
2634
|
+
if service_token.to_s.empty?
|
2635
|
+
print yellow,"No api token found.",reset,"\n"
|
2636
|
+
return 1
|
2637
|
+
else
|
2638
|
+
print cyan,service_token,reset,"\n"
|
2639
|
+
return 0
|
2640
|
+
end
|
2641
|
+
end
|
2642
|
+
|
2643
|
+
title = "Cluster API Token: #{cluster['name']}"
|
2644
|
+
subtitles = []
|
2645
|
+
print_h1 title, subtitles, options
|
2646
|
+
|
2647
|
+
if service_token.to_s.empty?
|
2648
|
+
print yellow,"No api token found.",reset,"\n\n"
|
2649
|
+
return 1
|
2650
|
+
else
|
2651
|
+
print cyan,service_token,reset,"\n\n"
|
2652
|
+
return 0
|
2653
|
+
end
|
2654
|
+
rescue RestClient::Exception => e
|
2655
|
+
print_rest_exception(e, options)
|
2656
|
+
exit 1
|
2657
|
+
end
|
2658
|
+
end
|
2659
|
+
|
2660
|
+
def wiki(args)
|
2661
|
+
options = {}
|
2662
|
+
params = {}
|
2663
|
+
open_wiki_link = false
|
2664
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
2665
|
+
opts.banner = subcommand_usage("[cluster]")
|
2666
|
+
opts.on('--view', '--view', "View wiki page in web browser.") do
|
2667
|
+
open_wiki_link = true
|
2668
|
+
end
|
2669
|
+
build_common_options(opts, options, [:json, :dry_run, :remote])
|
2670
|
+
opts.footer = "View wiki page details for a cluster." + "\n" +
|
2671
|
+
"[cluster] is required. This is the name or id of a cluster."
|
2672
|
+
end
|
2673
|
+
optparse.parse!(args)
|
2674
|
+
if args.count != 1
|
2675
|
+
puts_error "#{Morpheus::Terminal.angry_prompt}wrong number of arguments. Expected 1 and received #{args.count} #{args.inspect}\n#{optparse}"
|
2676
|
+
return 1
|
2677
|
+
end
|
2678
|
+
connect(options)
|
2679
|
+
|
2680
|
+
begin
|
2681
|
+
cluster = find_cluster_by_name_or_id(args[0])
|
2682
|
+
return 1 if cluster.nil?
|
2683
|
+
|
2684
|
+
|
2685
|
+
@clusters_interface.setopts(options)
|
2686
|
+
if options[:dry_run]
|
2687
|
+
print_dry_run @clusters_interface.dry.wiki(cluster["id"], params)
|
2688
|
+
return
|
2689
|
+
end
|
2690
|
+
json_response = @clusters_interface.wiki(cluster["id"], params)
|
2691
|
+
page = json_response['page']
|
2692
|
+
|
2693
|
+
render_result = render_with_format(json_response, options, 'page')
|
2694
|
+
return 0 if render_result
|
2695
|
+
|
2696
|
+
if page
|
2697
|
+
|
2698
|
+
# my_terminal.exec("wiki get #{page['id']}")
|
2699
|
+
|
2700
|
+
print_h1 "cluster Wiki Page: #{cluster['name']}"
|
2701
|
+
# print_h1 "Wiki Page Details"
|
2702
|
+
print cyan
|
2703
|
+
|
2704
|
+
print_description_list({
|
2705
|
+
"Page ID" => 'id',
|
2706
|
+
"Name" => 'name',
|
2707
|
+
#"Category" => 'category',
|
2708
|
+
#"Ref Type" => 'refType',
|
2709
|
+
#"Ref ID" => 'refId',
|
2710
|
+
#"Owner" => lambda {|it| it['account'] ? it['account']['name'] : '' },
|
2711
|
+
"Created" => lambda {|it| format_local_dt(it['dateCreated']) },
|
2712
|
+
"Created By" => lambda {|it| it['createdBy'] ? it['createdBy']['username'] : '' },
|
2713
|
+
"Updated" => lambda {|it| format_local_dt(it['lastUpdated']) },
|
2714
|
+
"Updated By" => lambda {|it| it['updatedBy'] ? it['updatedBy']['username'] : '' }
|
2715
|
+
}, page)
|
2716
|
+
print reset,"\n"
|
2717
|
+
|
2718
|
+
print_h2 "Page Content"
|
2719
|
+
print cyan, page['content'], reset, "\n"
|
2720
|
+
|
2721
|
+
else
|
2722
|
+
print "\n"
|
2723
|
+
print cyan, "No wiki page found.", reset, "\n"
|
2724
|
+
end
|
2725
|
+
print reset,"\n"
|
2726
|
+
|
2727
|
+
if open_wiki_link
|
2728
|
+
return view_wiki([args[0]])
|
2729
|
+
end
|
2730
|
+
|
2731
|
+
return 0
|
2732
|
+
rescue RestClient::Exception => e
|
2733
|
+
print_rest_exception(e, options)
|
2734
|
+
exit 1
|
2735
|
+
end
|
2736
|
+
end
|
2737
|
+
|
2738
|
+
def view_wiki(args)
|
2739
|
+
params = {}
|
2740
|
+
options = {}
|
2741
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
2742
|
+
opts.banner = subcommand_usage("[id]")
|
2743
|
+
build_common_options(opts, options, [:dry_run, :remote])
|
2744
|
+
opts.footer = "View cluster wiki page in a web browser" + "\n" +
|
2745
|
+
"[cluster] is required. This is the name or id of a cluster."
|
2746
|
+
end
|
2747
|
+
optparse.parse!(args)
|
2748
|
+
if args.count != 1
|
2749
|
+
raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
|
2750
|
+
end
|
2751
|
+
connect(options)
|
2752
|
+
begin
|
2753
|
+
cluster = find_cluster_by_name_or_id(args[0])
|
2754
|
+
return 1 if cluster.nil?
|
2755
|
+
|
2756
|
+
link = "#{@appliance_url}/login/oauth-redirect?access_token=#{@access_token}\\&redirectUri=/infrastructure/clusters/#{cluster['id']}#!wiki"
|
2757
|
+
|
2758
|
+
open_command = nil
|
2759
|
+
if RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/
|
2760
|
+
open_command = "start #{link}"
|
2761
|
+
elsif RbConfig::CONFIG['host_os'] =~ /darwin/
|
2762
|
+
open_command = "open #{link}"
|
2763
|
+
elsif RbConfig::CONFIG['host_os'] =~ /linux|bsd/
|
2764
|
+
open_command = "xdg-open #{link}"
|
2765
|
+
end
|
2766
|
+
|
2767
|
+
if options[:dry_run]
|
2768
|
+
puts "system: #{open_command}"
|
2769
|
+
return 0
|
2770
|
+
end
|
2771
|
+
|
2772
|
+
system(open_command)
|
2773
|
+
|
2774
|
+
return 0
|
2775
|
+
rescue RestClient::Exception => e
|
2776
|
+
print_rest_exception(e, options)
|
2777
|
+
exit 1
|
2778
|
+
end
|
2779
|
+
end
|
2780
|
+
|
2781
|
+
def update_wiki(args)
|
2782
|
+
options = {}
|
2783
|
+
params = {}
|
2784
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
2785
|
+
opts.banner = subcommand_usage("[cluster] [options]")
|
2786
|
+
build_option_type_options(opts, options, update_wiki_page_option_types)
|
2787
|
+
opts.on('--file FILE', "File containing the wiki content. This can be used instead of --content") do |filename|
|
2788
|
+
full_filename = File.expand_path(filename)
|
2789
|
+
if File.exists?(full_filename)
|
2790
|
+
params['content'] = File.read(full_filename)
|
2791
|
+
else
|
2792
|
+
print_red_alert "File not found: #{full_filename}"
|
2793
|
+
return 1
|
2794
|
+
end
|
2795
|
+
# use the filename as the name by default.
|
2796
|
+
if !params['name']
|
2797
|
+
params['name'] = File.basename(full_filename)
|
2798
|
+
end
|
2799
|
+
end
|
2800
|
+
opts.on(nil, '--clear', "Clear current page content") do |val|
|
2801
|
+
params['content'] = ""
|
2802
|
+
end
|
2803
|
+
build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote])
|
2804
|
+
end
|
2805
|
+
optparse.parse!(args)
|
2806
|
+
if args.count != 1
|
2807
|
+
puts_error "#{Morpheus::Terminal.angry_prompt}wrong number of arguments. Expected 1 and received #{args.count} #{args.inspect}\n#{optparse}"
|
2808
|
+
return 1
|
2809
|
+
end
|
2810
|
+
connect(options)
|
2811
|
+
|
2812
|
+
begin
|
2813
|
+
cluster = find_cluster_by_name_or_id(args[0])
|
2814
|
+
return 1 if cluster.nil?
|
2815
|
+
# construct payload
|
2816
|
+
passed_options = options[:options] ? options[:options].reject {|k,v| k.is_a?(Symbol) } : {}
|
2817
|
+
payload = nil
|
2818
|
+
if options[:payload]
|
2819
|
+
payload = options[:payload]
|
2820
|
+
payload.deep_merge!({'page' => passed_options}) unless passed_options.empty?
|
2821
|
+
else
|
2822
|
+
payload = {
|
2823
|
+
'page' => {
|
2824
|
+
}
|
2825
|
+
}
|
2826
|
+
# allow arbitrary -O options
|
2827
|
+
payload.deep_merge!({'page' => passed_options}) unless passed_options.empty?
|
2828
|
+
# prompt for options
|
2829
|
+
#params = Morpheus::Cli::OptionTypes.prompt(update_wiki_page_option_types, options[:options], @api_client, options[:params])
|
2830
|
+
#params = passed_options
|
2831
|
+
params.deep_merge!(passed_options)
|
2832
|
+
|
2833
|
+
if params.empty?
|
2834
|
+
raise_command_error "Specify at least one option to update.\n#{optparse}"
|
2835
|
+
end
|
2836
|
+
|
2837
|
+
payload.deep_merge!({'page' => params}) unless params.empty?
|
2838
|
+
end
|
2839
|
+
@clusters_interface.setopts(options)
|
2840
|
+
if options[:dry_run]
|
2841
|
+
print_dry_run @clusters_interface.dry.update_wiki(cluster["id"], payload)
|
2842
|
+
return
|
2843
|
+
end
|
2844
|
+
json_response = @clusters_interface.update_wiki(cluster["id"], payload)
|
2845
|
+
|
2846
|
+
if options[:json]
|
2847
|
+
puts as_json(json_response, options)
|
2848
|
+
else
|
2849
|
+
print_green_success "Updated wiki page for cluster #{cluster['name']}"
|
2850
|
+
wiki([cluster['id']])
|
2851
|
+
end
|
2852
|
+
return 0
|
2853
|
+
rescue RestClient::Exception => e
|
2854
|
+
print_rest_exception(e, options)
|
2855
|
+
exit 1
|
2856
|
+
end
|
2857
|
+
end
|
2858
|
+
|
2859
|
+
def history(args)
|
2860
|
+
raw_args = args.dup
|
2861
|
+
options = {}
|
2862
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
2863
|
+
opts.banner = subcommand_usage("[cluster]")
|
2864
|
+
opts.on( nil, '--events', "Display sub processes (events)." ) do
|
2865
|
+
options[:show_events] = true
|
2866
|
+
end
|
2867
|
+
opts.on( nil, '--output', "Display process output." ) do
|
2868
|
+
options[:show_output] = true
|
2869
|
+
end
|
2870
|
+
opts.on('--details', "Display more details: memory and storage usage used / max values." ) do
|
2871
|
+
options[:show_events] = true
|
2872
|
+
options[:show_output] = true
|
2873
|
+
options[:details] = true
|
2874
|
+
end
|
2875
|
+
opts.on('--process-id ID', String, "Display details about a specfic process only." ) do |val|
|
2876
|
+
options[:process_id] = val
|
2877
|
+
end
|
2878
|
+
opts.on('--event-id ID', String, "Display details about a specfic process event only." ) do |val|
|
2879
|
+
options[:event_id] = val
|
2880
|
+
end
|
2881
|
+
build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
|
2882
|
+
opts.footer = "List historical processes for a specific cluster.\n" +
|
2883
|
+
"[cluster] is required. This is the name or id of an cluster."
|
2884
|
+
end
|
2885
|
+
|
2886
|
+
optparse.parse!(args)
|
2887
|
+
|
2888
|
+
# shortcut to other actions
|
2889
|
+
if options[:process_id]
|
2890
|
+
return history_details(raw_args)
|
2891
|
+
elsif options[:event_id]
|
2892
|
+
return history_event_details(raw_args)
|
2893
|
+
end
|
2894
|
+
|
2895
|
+
if args.count != 1
|
2896
|
+
puts optparse
|
2897
|
+
return 1
|
2898
|
+
end
|
2899
|
+
connect(options)
|
2900
|
+
begin
|
2901
|
+
cluster = find_cluster_by_name_or_id(args[0])
|
2902
|
+
return 1 if cluster.nil?
|
2903
|
+
params = {}
|
2904
|
+
params.merge!(parse_list_options(options))
|
2905
|
+
# params[:query] = params.delete(:phrase) unless params[:phrase].nil?
|
2906
|
+
@clusters_interface.setopts(options)
|
2907
|
+
if options[:dry_run]
|
2908
|
+
print_dry_run @clusters_interface.dry.history(cluster['id'], params)
|
2909
|
+
return
|
2910
|
+
end
|
2911
|
+
json_response = @clusters_interface.history(cluster['id'], params)
|
2912
|
+
if options[:json]
|
2913
|
+
puts as_json(json_response, options, "processes")
|
2914
|
+
return 0
|
2915
|
+
elsif options[:yaml]
|
2916
|
+
puts as_yaml(json_response, options, "processes")
|
2917
|
+
return 0
|
2918
|
+
elsif options[:csv]
|
2919
|
+
puts records_as_csv(json_response['processes'], options)
|
2920
|
+
return 0
|
2921
|
+
else
|
2922
|
+
title = "Cluster History: #{cluster['name']}"
|
2923
|
+
subtitles = []
|
2924
|
+
if params[:query]
|
2925
|
+
subtitles << "Search: #{params[:query]}".strip
|
2926
|
+
end
|
2927
|
+
subtitles += parse_list_subtitles(options)
|
2928
|
+
print_h1 title, subtitles, options
|
2929
|
+
if json_response['processes'].empty?
|
2930
|
+
print "#{cyan}No process history found.#{reset}\n\n"
|
2931
|
+
else
|
2932
|
+
history_records = []
|
2933
|
+
json_response["processes"].each do |process|
|
2934
|
+
row = {
|
2935
|
+
id: process['id'],
|
2936
|
+
eventId: nil,
|
2937
|
+
uniqueId: process['uniqueId'],
|
2938
|
+
name: process['displayName'],
|
2939
|
+
description: process['description'],
|
2940
|
+
processType: process['processType'] ? (process['processType']['name'] || process['processType']['code']) : process['processTypeName'],
|
2941
|
+
createdBy: process['createdBy'] ? (process['createdBy']['displayName'] || process['createdBy']['username']) : '',
|
2942
|
+
startDate: format_local_dt(process['startDate']),
|
2943
|
+
duration: format_process_duration(process),
|
2944
|
+
status: format_process_status(process),
|
2945
|
+
error: format_process_error(process, options[:details] ? nil : 20),
|
2946
|
+
output: format_process_output(process, options[:details] ? nil : 20)
|
2947
|
+
}
|
2948
|
+
history_records << row
|
2949
|
+
process_events = process['events'] || process['processEvents']
|
2950
|
+
if options[:show_events]
|
2951
|
+
if process_events
|
2952
|
+
process_events.each do |process_event|
|
2953
|
+
event_row = {
|
2954
|
+
id: process['id'],
|
2955
|
+
eventId: process_event['id'],
|
2956
|
+
uniqueId: process_event['uniqueId'],
|
2957
|
+
name: process_event['displayName'], # blank like the UI
|
2958
|
+
description: process_event['description'],
|
2959
|
+
processType: process_event['processType'] ? (process_event['processType']['name'] || process_event['processType']['code']) : process['processTypeName'],
|
2960
|
+
createdBy: process_event['createdBy'] ? (process_event['createdBy']['displayName'] || process_event['createdBy']['username']) : '',
|
2961
|
+
startDate: format_local_dt(process_event['startDate']),
|
2962
|
+
duration: format_process_duration(process_event),
|
2963
|
+
status: format_process_status(process_event),
|
2964
|
+
error: format_process_error(process_event, options[:details] ? nil : 20),
|
2965
|
+
output: format_process_output(process_event, options[:details] ? nil : 20)
|
2966
|
+
}
|
2967
|
+
history_records << event_row
|
2968
|
+
end
|
2969
|
+
else
|
2970
|
+
|
2971
|
+
end
|
2972
|
+
end
|
2973
|
+
end
|
2974
|
+
columns = [
|
2975
|
+
{:id => {:display_name => "PROCESS ID"} },
|
2976
|
+
:name,
|
2977
|
+
:description,
|
2978
|
+
{:processType => {:display_name => "PROCESS TYPE"} },
|
2979
|
+
{:createdBy => {:display_name => "CREATED BY"} },
|
2980
|
+
{:startDate => {:display_name => "START DATE"} },
|
2981
|
+
{:duration => {:display_name => "ETA/DURATION"} },
|
2982
|
+
:status,
|
2983
|
+
:error
|
2984
|
+
]
|
2985
|
+
if options[:show_events]
|
2986
|
+
columns.insert(1, {:eventId => {:display_name => "EVENT ID"} })
|
2987
|
+
end
|
2988
|
+
if options[:show_output]
|
2989
|
+
columns << :output
|
2990
|
+
end
|
2991
|
+
# custom pretty table columns ...
|
2992
|
+
if options[:include_fields]
|
2993
|
+
columns = options[:include_fields]
|
2994
|
+
end
|
2995
|
+
print cyan
|
2996
|
+
print as_pretty_table(history_records, columns, options)
|
2997
|
+
print_results_pagination(json_response, {:label => "process", :n_label => "processes"})
|
2998
|
+
print reset, "\n"
|
2999
|
+
return 0
|
3000
|
+
end
|
3001
|
+
end
|
3002
|
+
rescue RestClient::Exception => e
|
3003
|
+
print_rest_exception(e, options)
|
3004
|
+
exit 1
|
3005
|
+
end
|
3006
|
+
end
|
3007
|
+
|
3008
|
+
def history_details(args)
|
3009
|
+
options = {}
|
3010
|
+
process_id = nil
|
3011
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
3012
|
+
opts.banner = subcommand_usage("[cluster] [process-id]")
|
3013
|
+
opts.on('--process-id ID', String, "Display details about a specfic event." ) do |val|
|
3014
|
+
options[:process_id] = val
|
3015
|
+
end
|
3016
|
+
opts.add_hidden_option('process-id')
|
3017
|
+
build_common_options(opts, options, [:query, :json, :yaml, :csv, :fields, :dry_run, :remote])
|
3018
|
+
opts.footer = "Display history details for a specific process.\n" +
|
3019
|
+
"[cluster] is required. This is the name or id of a cluster.\n" +
|
3020
|
+
"[process-id] is required. This is the id of the process."
|
3021
|
+
end
|
3022
|
+
optparse.parse!(args)
|
3023
|
+
if args.count == 2
|
3024
|
+
process_id = args[1]
|
3025
|
+
elsif args.count == 1 && options[:process_id]
|
3026
|
+
process_id = options[:process_id]
|
3027
|
+
else
|
3028
|
+
puts_error optparse
|
3029
|
+
return 1
|
3030
|
+
end
|
3031
|
+
connect(options)
|
3032
|
+
begin
|
3033
|
+
cluster = find_cluster_by_name_or_id(args[0])
|
3034
|
+
return 1 if cluster.nil?
|
3035
|
+
params = {}
|
3036
|
+
params.merge!(parse_list_options(options))
|
3037
|
+
params[:query] = params.delete(:phrase) unless params[:phrase].nil?
|
3038
|
+
@clusters_interface.setopts(options)
|
3039
|
+
if options[:dry_run]
|
3040
|
+
print_dry_run @clusters_interface.dry.history_details(cluster['id'], process_id, params)
|
3041
|
+
return
|
3042
|
+
end
|
3043
|
+
json_response = @clusters_interface.history_details(cluster['id'], process_id, params)
|
3044
|
+
if options[:json]
|
3045
|
+
puts as_json(json_response, options, "process")
|
3046
|
+
return 0
|
3047
|
+
elsif options[:yaml]
|
3048
|
+
puts as_yaml(json_response, options, "process")
|
3049
|
+
return 0
|
3050
|
+
elsif options[:csv]
|
3051
|
+
puts records_as_csv(json_response['process'], options)
|
3052
|
+
return 0
|
3053
|
+
else
|
3054
|
+
process = json_response["process"]
|
3055
|
+
title = "Cluster History Details"
|
3056
|
+
subtitles = []
|
3057
|
+
subtitles << " Process ID: #{process_id}"
|
3058
|
+
subtitles += parse_list_subtitles(options)
|
3059
|
+
print_h1 title, subtitles, options
|
3060
|
+
print_process_details(process)
|
3061
|
+
|
3062
|
+
print_h2 "Process Events", options
|
3063
|
+
process_events = process['events'] || process['processEvents'] || []
|
3064
|
+
history_records = []
|
3065
|
+
if process_events.empty?
|
3066
|
+
puts "#{cyan}No events found.#{reset}"
|
3067
|
+
else
|
3068
|
+
process_events.each do |process_event|
|
3069
|
+
event_row = {
|
3070
|
+
id: process_event['id'],
|
3071
|
+
eventId: process_event['id'],
|
3072
|
+
uniqueId: process_event['uniqueId'],
|
3073
|
+
name: process_event['displayName'], # blank like the UI
|
3074
|
+
description: process_event['description'],
|
3075
|
+
processType: process_event['processType'] ? (process_event['processType']['name'] || process_event['processType']['code']) : process['processTypeName'],
|
3076
|
+
createdBy: process_event['createdBy'] ? (process_event['createdBy']['displayName'] || process_event['createdBy']['username']) : '',
|
3077
|
+
startDate: format_local_dt(process_event['startDate']),
|
3078
|
+
duration: format_process_duration(process_event),
|
3079
|
+
status: format_process_status(process_event),
|
3080
|
+
error: format_process_error(process_event),
|
3081
|
+
output: format_process_output(process_event)
|
3082
|
+
}
|
3083
|
+
history_records << event_row
|
3084
|
+
end
|
3085
|
+
columns = [
|
3086
|
+
{:id => {:display_name => "EVENT ID"} },
|
3087
|
+
:name,
|
3088
|
+
:description,
|
3089
|
+
{:processType => {:display_name => "PROCESS TYPE"} },
|
3090
|
+
{:createdBy => {:display_name => "CREATED BY"} },
|
3091
|
+
{:startDate => {:display_name => "START DATE"} },
|
3092
|
+
{:duration => {:display_name => "ETA/DURATION"} },
|
3093
|
+
:status,
|
3094
|
+
:error,
|
3095
|
+
:output
|
3096
|
+
]
|
3097
|
+
print cyan
|
3098
|
+
print as_pretty_table(history_records, columns, options)
|
3099
|
+
print_results_pagination({size: process_events.size, total: process_events.size})
|
3100
|
+
print reset, "\n"
|
3101
|
+
return 0
|
3102
|
+
end
|
3103
|
+
end
|
3104
|
+
rescue RestClient::Exception => e
|
3105
|
+
print_rest_exception(e, options)
|
3106
|
+
exit 1
|
3107
|
+
end
|
3108
|
+
end
|
3109
|
+
|
3110
|
+
def history_event_details(args)
|
3111
|
+
options = {}
|
3112
|
+
process_event_id = nil
|
3113
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
3114
|
+
opts.banner = subcommand_usage("[cluster] [event-id]")
|
3115
|
+
opts.on('--event-id ID', String, "Display details about a specfic event." ) do |val|
|
3116
|
+
options[:event_id] = val
|
3117
|
+
end
|
3118
|
+
opts.add_hidden_option('event-id')
|
3119
|
+
build_common_options(opts, options, [:query, :json, :yaml, :csv, :fields, :dry_run, :remote])
|
3120
|
+
opts.footer = "Display history details for a specific process event.\n" +
|
3121
|
+
"[cluster] is required. This is the name or id of an cluster.\n" +
|
3122
|
+
"[event-id] is required. This is the id of the process event."
|
3123
|
+
end
|
3124
|
+
optparse.parse!(args)
|
3125
|
+
if args.count == 2
|
3126
|
+
process_event_id = args[1]
|
3127
|
+
elsif args.count == 1 && options[:event_id]
|
3128
|
+
process_event_id = options[:event_id]
|
3129
|
+
else
|
3130
|
+
puts_error optparse
|
3131
|
+
return 1
|
3132
|
+
end
|
3133
|
+
connect(options)
|
3134
|
+
begin
|
3135
|
+
cluster = find_cluster_by_name_or_id(args[0])
|
3136
|
+
return 1 if cluster.nil?
|
3137
|
+
params = {}
|
3138
|
+
params.merge!(parse_list_options(options))
|
3139
|
+
@clusters_interface.setopts(options)
|
3140
|
+
if options[:dry_run]
|
3141
|
+
print_dry_run @clusters_interface.dry.history_event_details(cluster['id'], process_event_id, params)
|
3142
|
+
return
|
3143
|
+
end
|
3144
|
+
json_response = @clusters_interface.history_event_details(cluster['id'], process_event_id, params)
|
3145
|
+
if options[:json]
|
3146
|
+
puts as_json(json_response, options, "processEvent")
|
3147
|
+
return 0
|
3148
|
+
elsif options[:yaml]
|
3149
|
+
puts as_yaml(json_response, options, "processEvent")
|
3150
|
+
return 0
|
3151
|
+
elsif options[:csv]
|
3152
|
+
puts records_as_csv(json_response['processEvent'], options)
|
3153
|
+
return 0
|
3154
|
+
else
|
3155
|
+
process_event = json_response['processEvent'] || json_response['event']
|
3156
|
+
title = "Cluster History Event"
|
3157
|
+
subtitles = []
|
3158
|
+
subtitles += parse_list_subtitles(options)
|
3159
|
+
print_h1 title, subtitles, options
|
3160
|
+
print_process_event_details(process_event)
|
3161
|
+
print reset, "\n"
|
3162
|
+
return 0
|
3163
|
+
end
|
3164
|
+
rescue RestClient::Exception => e
|
3165
|
+
print_rest_exception(e, options)
|
3166
|
+
exit 1
|
3167
|
+
end
|
3168
|
+
end
|
3169
|
+
|
3170
|
+
private
|
3171
|
+
|
3172
|
+
def print_process_event_details(process_event, options={})
|
3173
|
+
# process_event =~ process
|
3174
|
+
description_cols = {
|
3175
|
+
"Process ID" => lambda {|it| it['processId'] },
|
3176
|
+
"Event ID" => lambda {|it| it['id'] },
|
3177
|
+
"Name" => lambda {|it| it['displayName'] },
|
3178
|
+
"Description" => lambda {|it| it['description'] },
|
3179
|
+
"Process Type" => lambda {|it| it['processType'] ? (it['processType']['name'] || it['processType']['code']) : it['processTypeName'] },
|
3180
|
+
"Created By" => lambda {|it| it['createdBy'] ? (it['createdBy']['displayName'] || it['createdBy']['username']) : '' },
|
3181
|
+
"Start Date" => lambda {|it| format_local_dt(it['startDate']) },
|
3182
|
+
"End Date" => lambda {|it| format_local_dt(it['endDate']) },
|
3183
|
+
"Duration" => lambda {|it| format_process_duration(it) },
|
3184
|
+
"Status" => lambda {|it| format_process_status(it) },
|
3185
|
+
}
|
3186
|
+
print_description_list(description_cols, process_event)
|
3187
|
+
|
3188
|
+
if process_event['error']
|
3189
|
+
print_h2 "Error", options
|
3190
|
+
print reset
|
3191
|
+
#puts format_process_error(process_event)
|
3192
|
+
puts process_event['error'].to_s.strip
|
3193
|
+
end
|
3194
|
+
|
3195
|
+
if process_event['output']
|
3196
|
+
print_h2 "Output", options
|
3197
|
+
print reset
|
3198
|
+
#puts format_process_error(process_event)
|
3199
|
+
puts process_event['output'].to_s.strip
|
3200
|
+
end
|
3201
|
+
end
|
3202
|
+
|
3203
|
+
def print_clusters_table(clusters, opts={})
|
3204
|
+
table_color = opts[:color] || cyan
|
3205
|
+
rows = clusters.collect do |cluster|
|
3206
|
+
{
|
3207
|
+
id: cluster['id'],
|
3208
|
+
name: cluster['name'],
|
3209
|
+
type: (cluster['type']['name'] rescue ''),
|
3210
|
+
layout: (cluster['layout']['name'] rescue ''),
|
3211
|
+
workers: cluster['workerCount'],
|
3212
|
+
cloud: (cluster['zone']['name'] rescue ''),
|
3213
|
+
status: format_cluster_status(cluster)
|
3214
|
+
}
|
3215
|
+
end
|
3216
|
+
columns = [
|
3217
|
+
:id, :name, :type, :layout, :workers, :cloud, :status
|
3218
|
+
]
|
3219
|
+
print as_pretty_table(rows, columns, opts)
|
3220
|
+
end
|
3221
|
+
|
3222
|
+
def format_cluster_status(cluster, return_color=cyan)
|
3223
|
+
out = ""
|
3224
|
+
status_string = cluster['status']
|
3225
|
+
if cluster['enabled'] == false
|
3226
|
+
out << "#{red}DISABLED#{cluster['statusMessage'] ? "#{return_color} - #{cluster['statusMessage']}" : ''}#{return_color}"
|
3227
|
+
elsif status_string.nil? || status_string.empty? || status_string == "unknown"
|
3228
|
+
out << "#{white}UNKNOWN#{cluster['statusMessage'] ? "#{return_color} - #{cluster['statusMessage']}" : ''}#{return_color}"
|
3229
|
+
elsif status_string == 'ok'
|
3230
|
+
out << "#{green}#{status_string.upcase}#{return_color}"
|
3231
|
+
elsif status_string == 'syncing' || status_string == 'removing' || status_string.include?('provision')
|
3232
|
+
out << "#{yellow}#{status_string.upcase}#{return_color}"
|
3233
|
+
else
|
3234
|
+
out << "#{red}#{status_string ? status_string.upcase : 'N/A'}#{cluster['statusMessage'] ? "#{return_color} - #{cluster['statusMessage']}" : ''}#{return_color}"
|
3235
|
+
end
|
3236
|
+
out
|
3237
|
+
end
|
3238
|
+
|
3239
|
+
def format_server_power_state(server, return_color=cyan)
|
3240
|
+
out = ""
|
3241
|
+
if server['powerState'] == 'on'
|
3242
|
+
out << "#{green}ON#{return_color}"
|
3243
|
+
elsif server['powerState'] == 'off'
|
3244
|
+
out << "#{red}OFF#{return_color}"
|
3245
|
+
else
|
3246
|
+
out << "#{white}#{server['powerState'].to_s.upcase}#{return_color}"
|
3247
|
+
end
|
3248
|
+
out
|
3249
|
+
end
|
3250
|
+
|
3251
|
+
def format_server_status(server, return_color=cyan)
|
3252
|
+
out = ""
|
3253
|
+
status_string = server['status']
|
3254
|
+
# todo: colorize, upcase?
|
3255
|
+
out << status_string.to_s
|
3256
|
+
out
|
3257
|
+
end
|
3258
|
+
|
3259
|
+
def find_cluster_by_name_or_id(val)
|
3260
|
+
if val.to_s =~ /\A\d{1,}\Z/
|
3261
|
+
find_cluster_by_id(val)
|
3262
|
+
else
|
3263
|
+
find_cluster_by_name(val)
|
3264
|
+
end
|
3265
|
+
end
|
3266
|
+
|
3267
|
+
def find_cluster_by_id(id)
|
3268
|
+
json_results = @clusters_interface.get(id.to_i)
|
3269
|
+
if json_results['cluster'].empty?
|
3270
|
+
print_red_alert "Cluster not found by id #{id}"
|
3271
|
+
exit 1
|
3272
|
+
end
|
3273
|
+
json_results['cluster']
|
3274
|
+
end
|
3275
|
+
|
3276
|
+
def find_cluster_by_name(name)
|
3277
|
+
json_results = @clusters_interface.list({name: name})
|
3278
|
+
if json_results['clusters'].empty? || json_results['clusters'].count > 1
|
3279
|
+
print_red_alert "Cluster not found by name #{name}"
|
3280
|
+
exit 1
|
3281
|
+
end
|
3282
|
+
json_results['clusters'][0]
|
3283
|
+
end
|
3284
|
+
|
3285
|
+
def find_container_by_name_or_id(cluster_id, val)
|
3286
|
+
if val.to_s =~ /\A\d{1,}\Z/
|
3287
|
+
params = {"containerId": val.to_i}
|
3288
|
+
else
|
3289
|
+
params = {phrase: val}
|
3290
|
+
end
|
3291
|
+
json_results = @clusters_interface.list_containers(cluster_id, params)
|
3292
|
+
json_results["containers"].empty? ? nil : json_results["containers"][0]
|
3293
|
+
end
|
3294
|
+
|
3295
|
+
def find_container_group_by_name_or_id(cluster_id, resource_type, val)
|
3296
|
+
if val.to_s =~ /\A\d{1,}\Z/
|
3297
|
+
params = {"#{resource_type}Id": val.to_i}
|
3298
|
+
else
|
3299
|
+
params = {phrase: val}
|
3300
|
+
end
|
3301
|
+
json_results = @clusters_interface.list_container_groups(cluster_id, resource_type, params)
|
3302
|
+
json_results["#{resource_type}s"].empty? ? nil : json_results["#{resource_type}s"][0]
|
3303
|
+
end
|
3304
|
+
|
3305
|
+
def find_volume_by_name_or_id(cluster_id, val)
|
3306
|
+
if val.to_s =~ /\A\d{1,}\Z/
|
3307
|
+
params = {volumeId: val.to_i}
|
3308
|
+
else
|
3309
|
+
params = {phrase: val}
|
3310
|
+
end
|
3311
|
+
json_results = @clusters_interface.list_volumes(cluster_id, params)
|
3312
|
+
json_results['volumes'].empty? ? nil : json_results['volumes'][0]
|
3313
|
+
end
|
3314
|
+
|
3315
|
+
def find_service_by_name_or_id(cluster_id, val)
|
3316
|
+
if val.to_s =~ /\A\d{1,}\Z/
|
3317
|
+
params = {serviceId: val.to_i}
|
3318
|
+
else
|
3319
|
+
params = {phrase: val}
|
3320
|
+
end
|
3321
|
+
json_results = @clusters_interface.list_services(cluster_id, params)
|
3322
|
+
json_results['services'].empty? ? nil : json_results['services'][0]
|
3323
|
+
end
|
3324
|
+
|
3325
|
+
def find_namespace_by_name_or_id(cluster_id, val)
|
3326
|
+
if val.to_s =~ /\A\d{1,}\Z/
|
3327
|
+
params = {namespaceId: val.to_i}
|
3328
|
+
else
|
3329
|
+
params = {phrase: val}
|
3330
|
+
end
|
3331
|
+
json_results = @clusters_interface.list_namespaces(cluster_id, params)
|
3332
|
+
json_results['namespaces'].empty? ? nil : json_results['namespaces'][0]
|
3333
|
+
end
|
3334
|
+
|
3335
|
+
def find_job_by_name_or_id(cluster_id, val)
|
3336
|
+
if val.to_s =~ /\A\d{1,}\Z/
|
3337
|
+
params = {jobId: val.to_i}
|
3338
|
+
else
|
3339
|
+
params = {phrase: val}
|
3340
|
+
end
|
3341
|
+
json_results = @clusters_interface.list_jobs(cluster_id, params)
|
3342
|
+
json_results['jobs'].empty? ? nil : json_results['jobs'][0]
|
3343
|
+
end
|
3344
|
+
|
3345
|
+
def find_cluster_type_by_name_or_id(val)
|
3346
|
+
(val.to_s =~ /\A\d{1,}\Z/) ? find_cluster_type_by_id(val) : find_cluster_type_by_name(val)
|
3347
|
+
end
|
3348
|
+
|
3349
|
+
def find_cluster_type_by_id(id)
|
3350
|
+
get_cluster_types.find { |it| it['id'] == id }
|
3351
|
+
end
|
3352
|
+
|
3353
|
+
def find_cluster_type_by_name(name)
|
3354
|
+
get_cluster_types.find { |it| it['name'].downcase == name.downcase || it['code'].downcase == name.downcase }
|
3355
|
+
end
|
3356
|
+
|
3357
|
+
def cluster_types_for_dropdown
|
3358
|
+
get_cluster_types.collect {|it| {'id' => it['id'], 'name' => it['name'], 'code' => it['code'], 'value' => it['code']} }
|
3359
|
+
end
|
3360
|
+
|
3361
|
+
def get_cluster_types(refresh=false)
|
3362
|
+
if !@cluster_types || refresh
|
3363
|
+
@cluster_types = @clusters_interface.cluster_types()['clusterTypes']
|
3364
|
+
end
|
3365
|
+
@cluster_types
|
3366
|
+
end
|
3367
|
+
|
3368
|
+
def find_layout_by_name_or_id(val)
|
3369
|
+
(val.to_s =~ /\A\d{1,}\Z/) ? find_layout_by_id(val) : find_layout_by_name(val)
|
3370
|
+
end
|
3371
|
+
|
3372
|
+
def find_layout_by_id(id)
|
3373
|
+
@compute_type_layouts_interface.get(id)['layout'] rescue nil
|
3374
|
+
end
|
3375
|
+
|
3376
|
+
def find_layout_by_name(name)
|
3377
|
+
@compute_type_layouts_interface.list({phrase:name}).find { it['name'].downcase == name.downcase || it['code'].downcase == name.downcase }
|
3378
|
+
end
|
3379
|
+
|
3380
|
+
def layouts_for_dropdown(zone_id, group_type_id)
|
3381
|
+
@compute_type_layouts_interface.list({zoneId: zone_id, groupTypeId: group_type_id})["layouts"].collect { |it| {'id' => it['id'], 'name' => it['name'], 'value' => it['id'], 'code' => it['code']} }
|
3382
|
+
end
|
3383
|
+
|
3384
|
+
def find_service_plan_by_name_or_id(val)
|
3385
|
+
(val.to_s =~ /\A\d{1,}\Z/) ? find_service_plan_by_id(val) : find_service_plan_by_name(val)
|
3386
|
+
end
|
3387
|
+
|
3388
|
+
def find_service_plan_by_id(id)
|
3389
|
+
@servers_interface.service_plan(id)['servicePlan'] rescue nil
|
3390
|
+
end
|
3391
|
+
|
3392
|
+
def find_service_plan_by_name(name)
|
3393
|
+
@servers_interface.service_plan({phrase: name})['servicePlans'].find { |it| it['name'].downcase == name.downcase || it['code'].downcase == name.downcase } rescue nil
|
3394
|
+
end
|
3395
|
+
|
3396
|
+
def find_security_group_by_name(val)
|
3397
|
+
@security_groups_interface.list({phrase: val})['securityGroups'][0] rescue nil
|
3398
|
+
end
|
3399
|
+
|
3400
|
+
def find_cloud_resource_pool_by_name_or_id(cloud_id, val)
|
3401
|
+
(val.to_s =~ /\A\d{1,}\Z/) ? find_cloud_resource_pool_by_id(cloud_id, val) : find_cloud_resource_pool_by_name(cloud_id, val)
|
3402
|
+
end
|
3403
|
+
|
3404
|
+
def find_cloud_resource_pool_by_name(cloud_id, name)
|
3405
|
+
get_cloud_resource_pools(cloud_id).find { |it| it['name'].downcase == name.downcase } rescue nil
|
3406
|
+
end
|
3407
|
+
|
3408
|
+
def find_cloud_resource_pool_by_id(cloud_id, id)
|
3409
|
+
get_cloud_resource_pools(cloud_id).find { |it| it['id'] == id } rescue nil
|
3410
|
+
end
|
3411
|
+
|
3412
|
+
def get_cloud_resource_pools(cloud_id, refresh=false)
|
3413
|
+
if !@cloud_resource_pools || refresh
|
3414
|
+
@cloud_resource_pools = @cloud_resource_pools_interface.list(cloud_id)['resourcePools']
|
3415
|
+
end
|
3416
|
+
@cloud_resource_pools
|
3417
|
+
end
|
3418
|
+
|
3419
|
+
def find_server_type_by_name_or_id(val)
|
3420
|
+
(val.to_s =~ /\A\d{1,}\Z/) ? find_server_type_by_name(val) : find_server_type_by_id(val)
|
3421
|
+
end
|
3422
|
+
|
3423
|
+
def find_server_type_by_name(val)
|
3424
|
+
@server_types_interface.list({name: val})['serverTypes'][0] rescue nil
|
3425
|
+
end
|
3426
|
+
|
3427
|
+
def find_server_type_by_id(val)
|
3428
|
+
@server_types_interface.get(val)['serverType']
|
3429
|
+
end
|
3430
|
+
|
3431
|
+
def service_plans_for_dropdown(zone_id, provision_type_id)
|
3432
|
+
@servers_interface.service_plans({zoneId: zone_id, provisionTypeId: provision_type_id})['plans'] rescue []
|
3433
|
+
end
|
3434
|
+
|
3435
|
+
def namespace_service_plans
|
3436
|
+
@service_plans_interface.list({'provisionable' => 'any', 'provisionTypeId' => @provision_types_interface.list({'code' => 'docker'})['provisionTypes'].first['id']})['servicePlans'] rescue []
|
3437
|
+
end
|
3438
|
+
|
3439
|
+
def get_cloud_type(id)
|
3440
|
+
@clouds_interface.cloud_type(id)['zoneType']
|
3441
|
+
end
|
3442
|
+
|
3443
|
+
def get_provision_type_for_zone_type(zone_type_id)
|
3444
|
+
@clouds_interface.cloud_type(zone_type_id)['zoneType']['provisionTypes'].first rescue nil
|
3445
|
+
end
|
3446
|
+
|
3447
|
+
def current_user(refresh=false)
|
3448
|
+
if !@current_user || refresh
|
3449
|
+
load_whoami
|
3450
|
+
end
|
3451
|
+
@current_user
|
3452
|
+
end
|
3453
|
+
|
3454
|
+
def load_group(options)
|
3455
|
+
# Group / Site
|
3456
|
+
group_id = nil
|
3457
|
+
group = options[:group] ? find_group_by_name_or_id_for_provisioning(options[:group]) : nil
|
3458
|
+
|
3459
|
+
if group
|
3460
|
+
group_id = group["id"]
|
3461
|
+
else
|
3462
|
+
if @active_group_id
|
3463
|
+
group_id = @active_group_id
|
3464
|
+
else
|
3465
|
+
available_groups = get_available_groups
|
3466
|
+
|
3467
|
+
if available_groups.empty?
|
3468
|
+
print_red_alert "No available groups"
|
3469
|
+
exit 1
|
3470
|
+
elsif available_groups.count > 1 && !options[:no_prompt]
|
3471
|
+
group_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'group', 'type' => 'select', 'fieldLabel' => 'Group', 'selectOptions' => available_groups, 'required' => true, 'description' => 'Select Group.'}],options[:options],@api_client,{})['group']
|
3472
|
+
else
|
3473
|
+
group_id = available_groups.first['id']
|
3474
|
+
end
|
3475
|
+
end
|
3476
|
+
end
|
3477
|
+
@groups_interface.get(group_id)['group']
|
3478
|
+
end
|
3479
|
+
|
3480
|
+
def prompt_service_plan(zone_id, provision_type, options)
|
3481
|
+
available_service_plans = service_plans_for_dropdown(zone_id, provision_type['id'])
|
3482
|
+
if available_service_plans.empty?
|
3483
|
+
print_red_alert "Cloud #{zone_id} has no available plans"
|
3484
|
+
exit 1
|
3485
|
+
end
|
3486
|
+
if options[:servicePlan]
|
3487
|
+
service_plan = available_service_plans.find {|sp| sp['id'] == options[:servicePlan].to_i || sp['name'] == options[:servicePlan] || sp['code'] == options[:servicePlan] }
|
3488
|
+
else
|
3489
|
+
if available_service_plans.count > 1 && !options[:no_prompt]
|
3490
|
+
service_plan_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'servicePlan', 'type' => 'select', 'fieldLabel' => 'Plan', 'selectOptions' => available_service_plans, 'required' => true, 'description' => 'Select Plan.'}],options[:options],@api_client,{})['servicePlan'].to_i
|
3491
|
+
else
|
3492
|
+
service_plan_id = available_service_plans.first['id']
|
3493
|
+
end
|
3494
|
+
#service_plan = find_service_plan_by_id(service_plan_id)
|
3495
|
+
service_plan = available_service_plans.find {|sp| sp['id'] == service_plan_id.to_i || sp['name'] == service_plan_id.to_s || sp['code'] == service_plan_id.to_s }
|
3496
|
+
end
|
3497
|
+
service_plan
|
3498
|
+
end
|
3499
|
+
|
3500
|
+
def prompt_service_plan_options(service_plan, options)
|
3501
|
+
plan_options = {}
|
3502
|
+
|
3503
|
+
# custom max memory
|
3504
|
+
if service_plan['customMaxMemory']
|
3505
|
+
if !options[:maxMemory]
|
3506
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'maxMemory', 'type' => 'number', 'fieldLabel' => 'Max Memory (MB)', 'required' => false, 'description' => 'This will override any memory requirement set on the virtual image', 'defaultValue' => service_plan['maxMemory'] ? service_plan['maxMemory'] / (1024 * 1024) : 10 }], options[:options])
|
3507
|
+
plan_options['maxMemory'] = v_prompt['maxMemory'] * 1024 * 1024 if v_prompt['maxMemory']
|
3508
|
+
else
|
3509
|
+
plan_options['maxMemory'] = options[:maxMemory]
|
3510
|
+
end
|
3511
|
+
end
|
3512
|
+
|
3513
|
+
# custom cores: max cpu, max cores, cores per socket
|
3514
|
+
if service_plan['customCores']
|
3515
|
+
if options[:cpuCount].empty?
|
3516
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'cpuCount', 'type' => 'number', 'fieldLabel' => 'CPU Count', 'required' => false, 'description' => 'Set CPU Count', 'defaultValue' => service_plan['maxCpu'] ? service_plan['maxCpu'] : 1 }], options[:options])
|
3517
|
+
plan_options['cpuCount'] = v_prompt['cpuCount'] if v_prompt['cpuCount']
|
3518
|
+
else
|
3519
|
+
plan_options['cpuCount']
|
3520
|
+
end
|
3521
|
+
if options[:coreCount].empty?
|
3522
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'coreCount', 'type' => 'number', 'fieldLabel' => 'Core Count', 'required' => false, 'description' => 'Set Core Count', 'defaultValue' => service_plan['maxCores'] ? service_plan['maxCores'] : 1 }], options[:options])
|
3523
|
+
plan_options['coreCount'] = v_prompt['coreCount'] if v_prompt['coreCount']
|
3524
|
+
end
|
3525
|
+
if options[:coresPerSocket].empty? && service_plan['coresPerSocket']
|
3526
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'coresPerSocket', 'type' => 'number', 'fieldLabel' => 'Cores Per Socket', 'required' => false, 'description' => 'Set Core Per Socket', 'defaultValue' => service_plan['coresPerSocket']}], options[:options])
|
3527
|
+
plan_options['coresPerSocket'] = v_prompt['coresPerSocket'] if v_prompt['coresPerSocket']
|
3528
|
+
end
|
3529
|
+
end
|
3530
|
+
plan_options
|
3531
|
+
end
|
3532
|
+
|
3533
|
+
def add_server_options(opts, options)
|
3534
|
+
opts.on( '-c', '--cloud CLOUD', "Cloud Name or ID" ) do |val|
|
3535
|
+
options[:cloud] = val
|
3536
|
+
end
|
3537
|
+
opts.on( '--resource-pool ID', String, "ID of the Resource Pool for Amazon VPC and Azure Resource Group" ) do |val|
|
3538
|
+
options[:resourcePool] = val
|
3539
|
+
end
|
3540
|
+
opts.on( '-p', '--plan PLAN', "Service Plan") do |val|
|
3541
|
+
options[:servicePlan] = val
|
3542
|
+
end
|
3543
|
+
opts.on('--max-memory VALUE', String, "Maximum Memory (MB)") do |val|
|
3544
|
+
options[:maxMemory] = val
|
3545
|
+
end
|
3546
|
+
opts.on('--cpu-count VALUE', String, "CPU Count") do |val|
|
3547
|
+
options[:cpuCount] = val
|
3548
|
+
end
|
3549
|
+
opts.on('--core-count VALUE', String, "Core Count") do |val|
|
3550
|
+
options[:coreCount] = val
|
3551
|
+
end
|
3552
|
+
opts.on('--cores-per-socket VALUE', String, "Cores Per Socket") do |val|
|
3553
|
+
options[:coresPerSocket] = val
|
3554
|
+
end
|
3555
|
+
opts.on('--volumes JSON', String, "Volumes Config JSON") do |val|
|
3556
|
+
begin
|
3557
|
+
volumes = JSON.parse(val.to_s)
|
3558
|
+
rescue JSON::ParserError => e
|
3559
|
+
print_red_alert "Unable to parse volumes JSON"
|
3560
|
+
exit 1
|
3561
|
+
end
|
3562
|
+
options[:volumes] = volumes.kind_of?(Array) ? volumes : [volumes]
|
3563
|
+
end
|
3564
|
+
opts.on('--volumes-file FILE', String, "Volumes Config from a local JSON or YAML file") do |val|
|
3565
|
+
config_file = File.expand_path(val)
|
3566
|
+
if !File.exists?(config_file) || !File.file?(config_file)
|
3567
|
+
print_red_alert "Specified volumes file not found: #{config_file}"
|
3568
|
+
exit 1
|
3569
|
+
end
|
3570
|
+
if config_file =~ /\.ya?ml\Z/
|
3571
|
+
begin
|
3572
|
+
volumes = YAML.load_file(config_file)
|
3573
|
+
rescue YAML::ParseError
|
3574
|
+
print_red_alert "Unable to parse volumes YAML from: #{config_file}"
|
3575
|
+
exit 1
|
3576
|
+
end
|
3577
|
+
else
|
3578
|
+
volumes =
|
3579
|
+
begin
|
3580
|
+
volumes = JSON.parse(File.read(config_file))
|
3581
|
+
rescue JSON::ParserError
|
3582
|
+
print_red_alert "Unable to parse volumes JSON from: #{config_file}"
|
3583
|
+
exit 1
|
3584
|
+
end
|
3585
|
+
end
|
3586
|
+
options[:volumes] = volumes.kind_of?(Array) ? volumes : [volumes]
|
3587
|
+
end
|
3588
|
+
opts.on('--config-file FILE', String, "Instance Config from a local JSON or YAML file") do |val|
|
3589
|
+
options['configFile'] = val.to_s
|
3590
|
+
end
|
3591
|
+
opts.on('--network-interfaces JSON', String, "Network Interfaces Config JSON") do |val|
|
3592
|
+
begin
|
3593
|
+
networkInterfaces = JSON.parse(val.to_s)
|
3594
|
+
rescue JSON::ParserError => e
|
3595
|
+
print_red_alert "Unable to parse network interfaces JSON"
|
3596
|
+
exit 1
|
3597
|
+
end
|
3598
|
+
options[:networkInterfaces] = networkInterfaces.kind_of?(Array) ? networkInterfaces : [networkInterfaces]
|
3599
|
+
end
|
3600
|
+
opts.on('--network-interfaces-file FILE', String, "Network Interfaces Config from a local JSON or YAML file") do |val|
|
3601
|
+
config_file = File.expand_path(val)
|
3602
|
+
if !File.exists?(config_file) || !File.file?(config_file)
|
3603
|
+
print_red_alert "Specified network interfaces file not found: #{config_file}"
|
3604
|
+
exit 1
|
3605
|
+
end
|
3606
|
+
if config_file =~ /\.ya?ml\Z/
|
3607
|
+
begin
|
3608
|
+
networkInterfaces = YAML.load_file(config_file)
|
3609
|
+
rescue YAML::ParseError
|
3610
|
+
print_red_alert "Unable to parse network interfaces YAML from: #{config_file}"
|
3611
|
+
exit 1
|
3612
|
+
end
|
3613
|
+
else
|
3614
|
+
networkInterfaces =
|
3615
|
+
begin
|
3616
|
+
networkInterfaces = JSON.parse(File.read(config_file))
|
3617
|
+
rescue JSON::ParserError
|
3618
|
+
print_red_alert "Unable to parse network interfaces JSON from: #{config_file}"
|
3619
|
+
exit 1
|
3620
|
+
end
|
3621
|
+
end
|
3622
|
+
options[:networkInterfaces] = networkInterfaces.kind_of?(Array) ? networkInterfaces : [networkInterfaces]
|
3623
|
+
end
|
3624
|
+
opts.on('--security-groups LIST', Array, "Security Groups") do |list|
|
3625
|
+
options[:securityGroups] = list
|
3626
|
+
end
|
3627
|
+
opts.on("--create-user on|off", String, "User Config: Create Your User. Default is off") do |val|
|
3628
|
+
options[:createUser] = ['true','on','1'].include?(val.to_s)
|
3629
|
+
end
|
3630
|
+
opts.on("--user-group USERGROUP", String, "User Config: User Group") do |val|
|
3631
|
+
options[:userGroup] = val
|
3632
|
+
end
|
3633
|
+
opts.on('--domain VALUE', String, "Network Domain ID") do |val|
|
3634
|
+
options[:domain] = val
|
3635
|
+
end
|
3636
|
+
opts.on('--hostname VALUE', String, "Hostname") do |val|
|
3637
|
+
options[:hostname] = val
|
3638
|
+
end
|
3639
|
+
end
|
3640
|
+
|
3641
|
+
def add_perms_options(opts, options)
|
3642
|
+
opts.on('--group-access-all [on|off]', String, "Toggle Access for all groups.") do |val|
|
3643
|
+
options[:groupAccessAll] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == ''
|
3644
|
+
end
|
3645
|
+
opts.on('--group-access LIST', Array, "Group Access, comma separated list of group IDs.") do |list|
|
3646
|
+
if list.size == 1 && list[0] == 'null' # hacky way to clear it
|
3647
|
+
options[:groupAccessList] = []
|
3648
|
+
else
|
3649
|
+
options[:groupAccessList] = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
|
3650
|
+
end
|
3651
|
+
end
|
3652
|
+
opts.on('--group-defaults LIST', Array, "Group Default Selection, comma separated list of group IDs") do |list|
|
3653
|
+
if list.size == 1 && list[0] == 'null' # hacky way to clear it
|
3654
|
+
options[:groupDefaultsList] = []
|
3655
|
+
else
|
3656
|
+
options[:groupDefaultsList] = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
|
3657
|
+
end
|
3658
|
+
end
|
3659
|
+
opts.on('--plan-access-all [on|off]', String, "Toggle Access for all service plans.") do |val|
|
3660
|
+
options[:planAccessAll] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == ''
|
3661
|
+
end
|
3662
|
+
opts.on('--plan-access LIST', Array, "Service Plan Access, comma separated list of plan IDs.") do |list|
|
3663
|
+
if list.size == 1 && list[0] == 'null' # hacky way to clear it
|
3664
|
+
options[:planAccessList] = []
|
3665
|
+
else
|
3666
|
+
options[:planAccessList] = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
|
3667
|
+
end
|
3668
|
+
end
|
3669
|
+
opts.on('--plan-defaults LIST', Array, "Plan Default Selection, comma separated list of plan IDs") do |list|
|
3670
|
+
if list.size == 1 && list[0] == 'null' # hacky way to clear it
|
3671
|
+
options[:planDefaultsList] = []
|
3672
|
+
else
|
3673
|
+
options[:planDefaultsList] = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
|
3674
|
+
end
|
3675
|
+
end
|
3676
|
+
opts.on('--visibility [private|public]', String, "Visibility") do |val|
|
3677
|
+
options[:visibility] = val
|
3678
|
+
end
|
3679
|
+
opts.on('--tenants LIST', Array, "Tenant Access, comma separated list of account IDs") do |list|
|
3680
|
+
if list.size == 1 && list[0] == 'null' # hacky way to clear it
|
3681
|
+
options[:tenants] = []
|
3682
|
+
else
|
3683
|
+
options[:tenants] = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
|
3684
|
+
end
|
3685
|
+
end
|
3686
|
+
end
|
3687
|
+
|
3688
|
+
def prompt_resource_pool(group, cloud, service_plan, provision_type, options)
|
3689
|
+
resource_pool = nil
|
3690
|
+
|
3691
|
+
if provision_type && provision_type['hasZonePools']
|
3692
|
+
# Resource pool
|
3693
|
+
if !['resourcePoolId', 'resourceGroup', 'vpc'].find { |it| cloud['config'][it] && cloud['config'][it].length > 0 }
|
3694
|
+
resource_pool_id = nil
|
3695
|
+
resource_pool = options[:resourcePool] ? find_cloud_resource_pool_by_name_or_id(cloud['id'], options[:resourcePool]) : nil
|
3696
|
+
|
3697
|
+
if !resource_pool
|
3698
|
+
resource_pool_options = @options_interface.options_for_source('zonePools', {groupId: group['id'], zoneId: cloud['id'], planId: (service_plan['id'] rescue nil)})['data'].reject { |it| it['id'].nil? && it['name'].nil? }
|
3699
|
+
|
3700
|
+
if resource_pool_options.empty?
|
3701
|
+
print_red_alert "Cloud #{cloud['name']} has no available resource pools"
|
3702
|
+
exit 1
|
3703
|
+
elsif resource_pool_options.count > 1 && !options[:no_prompt]
|
3704
|
+
resource_pool_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'resourcePool', 'type' => 'select', 'fieldLabel' => 'Resource Pool', 'selectOptions' => resource_pool_options, 'required' => true, 'skipSingleOption' => true, 'description' => 'Select resource pool.'}],options[:options],api_client, {})['resourcePool']
|
3705
|
+
else
|
3706
|
+
resource_pool_id = resource_pool_options.first['id']
|
3707
|
+
end
|
3708
|
+
resource_pool = @cloud_resource_pools_interface.get(cloud['id'], resource_pool_id)['resourcePool']
|
3709
|
+
end
|
3710
|
+
end
|
3711
|
+
end
|
3712
|
+
resource_pool
|
3713
|
+
end
|
3714
|
+
|
3715
|
+
def prompt_security_groups_by_cloud(cloud, provision_type, resource_pool, options)
|
3716
|
+
security_groups = options[:securityGroups] || []
|
3717
|
+
|
3718
|
+
if security_groups.empty? && cloud['zoneType']['hasSecurityGroups'] && ['amazon', 'rds'].include?(provision_type['code']) && resource_pool
|
3719
|
+
available_security_groups = @api_client.options.options_for_source('zoneSecurityGroup', {zoneId: cloud['id'], poolId: resource_pool['id']})['data']
|
3720
|
+
|
3721
|
+
if available_security_groups.empty?
|
3722
|
+
#print_red_alert "Cloud #{cloud['name']} has no available plans"
|
3723
|
+
#exit 1
|
3724
|
+
elsif available_security_groups.count > 1 && !options[:no_prompt]
|
3725
|
+
while !available_security_groups.empty? && (security_groups.empty? || Morpheus::Cli::OptionTypes.confirm("Add security group?", {:default => false}))
|
3726
|
+
security_group = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'securityGroup', 'type' => 'select', 'fieldLabel' => 'Security Group', 'selectOptions' => available_security_groups, 'required' => true, 'description' => 'Select Security Group.'}], options[:options], @api_client, {})['securityGroup']
|
3727
|
+
security_groups << security_group
|
3728
|
+
available_security_groups.reject! { |sg| sg['value'] == security_group }
|
3729
|
+
end
|
3730
|
+
else
|
3731
|
+
security_groups << available_security_groups[0]
|
3732
|
+
end
|
3733
|
+
end
|
3734
|
+
security_groups
|
3735
|
+
end
|
3736
|
+
|
3737
|
+
def prompt_update_namespace(options)
|
3738
|
+
rtn = {}
|
3739
|
+
rtn['description'] = options[:description] || Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text', 'description' => 'Namespace Description', 'required' => false}], options[:options], @api_client)['description']
|
3740
|
+
rtn['active'] = options[:active].nil? ? (Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'active', 'fieldLabel' => 'Active', 'type' => 'checkbox', 'description' => 'Namespace Active', 'defaultValue' => true}], options[:options], @api_client))['active'] == 'on' : options[:active]
|
3741
|
+
|
3742
|
+
perms = prompt_permissions(options)
|
3743
|
+
if perms['resourcePool'] && !perms['resourcePool']['visibility'].nil?
|
3744
|
+
rtn['visibility'] = perms['resourcePool']['visibility']
|
3745
|
+
perms.delete('resourcePool')
|
3746
|
+
end
|
3747
|
+
rtn['permissions'] = perms
|
3748
|
+
rtn
|
3749
|
+
end
|
3750
|
+
|
3751
|
+
def prompt_permissions(options)
|
3752
|
+
all_groups = false
|
3753
|
+
group_access = []
|
3754
|
+
all_plans = false
|
3755
|
+
plan_access = []
|
3756
|
+
|
3757
|
+
# Group Access
|
3758
|
+
if options[:groupAccessAll]
|
3759
|
+
all_groups = true
|
3760
|
+
end
|
3761
|
+
|
3762
|
+
if !options[:groupAccessList].empty?
|
3763
|
+
group_access = options[:groupAccessList].collect {|site_id| {'id' => site_id.to_i}} || []
|
3764
|
+
elsif !options[:no_prompt]
|
3765
|
+
available_groups = get_available_groups
|
3766
|
+
|
3767
|
+
if available_groups.empty?
|
3768
|
+
#print_red_alert "No available groups"
|
3769
|
+
#exit 1
|
3770
|
+
elsif available_groups.count > 1
|
3771
|
+
available_groups.unshift({"id" => '0', "name" => "All", "value" => "all"})
|
3772
|
+
|
3773
|
+
# default to all
|
3774
|
+
group_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'group', 'type' => 'select', 'fieldLabel' => 'Group Access', 'selectOptions' => available_groups, 'required' => false, 'description' => 'Add Group Access.'}], options[:options], @api_client, {})['group']
|
3775
|
+
|
3776
|
+
if !group_id.nil?
|
3777
|
+
if group_id == 'all'
|
3778
|
+
all_groups = true
|
3779
|
+
else
|
3780
|
+
group_access = [{'id' => group_id, 'default' => Morpheus::Cli::OptionTypes.confirm("Set '#{available_groups.find{|it| it['id'] == group_id}['name']}' as default?", {:default => false})}]
|
3781
|
+
end
|
3782
|
+
available_groups = available_groups.reject {|it| it['value'] == group_id}
|
3783
|
+
|
3784
|
+
while !group_id.nil? && !available_groups.empty? && Morpheus::Cli::OptionTypes.confirm("Add another group access?", {:default => false})
|
3785
|
+
group_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'group', 'type' => 'select', 'fieldLabel' => 'Group Access', 'selectOptions' => available_groups, 'required' => false, 'description' => 'Add Group Access.'}], options[:options], @api_client, {})['group']
|
3786
|
+
|
3787
|
+
if !group_id.nil?
|
3788
|
+
if group_id == 'all'
|
3789
|
+
all_groups = true
|
3790
|
+
else
|
3791
|
+
group_access << {'id' => group_id, 'default' => Morpheus::Cli::OptionTypes.confirm("Set '#{available_groups.find{|it| it['id'] == group_id}['name']}' as default?", {:default => false})}
|
3792
|
+
end
|
3793
|
+
available_groups = available_groups.reject {|it| it['value'] == group_id}
|
3794
|
+
end
|
3795
|
+
end
|
3796
|
+
end
|
3797
|
+
end
|
3798
|
+
end
|
3799
|
+
|
3800
|
+
# Plan Access
|
3801
|
+
if options[:planAccessAll]
|
3802
|
+
all_plans = true
|
3803
|
+
end
|
3804
|
+
|
3805
|
+
if !options[:planAccessList].empty?
|
3806
|
+
plan_access = options[:planAccessList].collect {|plan_id| {'id' => plan_id.to_i}}
|
3807
|
+
elsif !options[:no_prompt]
|
3808
|
+
available_plans = namespace_service_plans.collect {|it| {'id' => it['id'], 'name' => it['name'], 'value' => it['id']} }
|
3809
|
+
|
3810
|
+
if available_plans.empty?
|
3811
|
+
#print_red_alert "No available plans"
|
3812
|
+
#exit 1
|
3813
|
+
elsif !available_plans.empty? && !options[:no_prompt]
|
3814
|
+
available_plans.unshift({"id" => '0', "name" => "All", "value" => "all"})
|
3815
|
+
|
3816
|
+
# default to all
|
3817
|
+
plan_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'plan', 'type' => 'select', 'fieldLabel' => 'Service Plan Access', 'selectOptions' => available_plans, 'required' => false, 'description' => 'Add Service Plan Access.'}], options[:options], @api_client, {})['plan']
|
3818
|
+
|
3819
|
+
if !plan_id.nil?
|
3820
|
+
if plan_id == 'all'
|
3821
|
+
all_plans = true
|
3822
|
+
else
|
3823
|
+
plan_access = [{'id' => plan_id, 'default' => Morpheus::Cli::OptionTypes.confirm("Set '#{available_plans.find{|it| it['id'] == plan_id}['name']}' as default?", {:default => false})}]
|
3824
|
+
end
|
3825
|
+
|
3826
|
+
available_plans = available_plans.reject {|it| it['value'] == plan_id}
|
3827
|
+
|
3828
|
+
while !plan_id.nil? && !available_plans.empty? && Morpheus::Cli::OptionTypes.confirm("Add another service plan access?", {:default => false})
|
3829
|
+
plan_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'plan', 'type' => 'select', 'fieldLabel' => 'Service Plan Access', 'selectOptions' => available_plans, 'required' => false, 'description' => 'Add Service Plan Access.'}], options[:options], @api_client, {})['plan']
|
3830
|
+
|
3831
|
+
if !plan_id.nil?
|
3832
|
+
if plan_id == 'all'
|
3833
|
+
all_plans = true
|
3834
|
+
else
|
3835
|
+
plan_access << {'id' => plan_id, 'default' => Morpheus::Cli::OptionTypes.confirm("Set '#{available_plans.find{|it| it['id'] == plan_id}['name']}' as default?", {:default => false})}
|
3836
|
+
end
|
3837
|
+
available_plans = available_plans.reject {|it| it['value'] == plan_id}
|
3838
|
+
end
|
3839
|
+
end
|
3840
|
+
end
|
3841
|
+
end
|
3842
|
+
end
|
3843
|
+
|
3844
|
+
resource_perms = {}
|
3845
|
+
resource_perms['all'] = true if all_groups
|
3846
|
+
resource_perms['sites'] = group_access if !group_access.empty?
|
3847
|
+
resource_perms['allPlans'] = true if all_plans
|
3848
|
+
resource_perms['plans'] = plan_access if !plan_access.empty?
|
3849
|
+
|
3850
|
+
permissions = {'resourcePermissions' => resource_perms}
|
3851
|
+
|
3852
|
+
available_accounts = @accounts_interface.list()['accounts'].collect {|it| {'name' => it['name'], 'value' => it['id']}}
|
3853
|
+
accounts = []
|
3854
|
+
|
3855
|
+
# Prompts for multi tenant
|
3856
|
+
if available_accounts.count > 1
|
3857
|
+
visibility = options[:visibility] || Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'visibility', 'fieldLabel' => 'Tenant Permissions Visibility', 'type' => 'select', 'defaultValue' => 'private', 'required' => true, 'selectOptions' => [{'name' => 'Private', 'value' => 'private'},{'name' => 'Public', 'value' => 'public'}]}], options[:options], @api_client, {})['visibility']
|
3858
|
+
|
3859
|
+
permissions['resourcePool'] = {'visibility' => visibility}
|
3860
|
+
|
3861
|
+
# Tenants
|
3862
|
+
if !options[:tenants].nil?
|
3863
|
+
accounts = options[:tenants].collect {|id| id.to_i}
|
3864
|
+
elsif !options[:no_prompt]
|
3865
|
+
account_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'account_id', 'type' => 'select', 'fieldLabel' => 'Add Tenant', 'selectOptions' => available_accounts, 'required' => false, 'description' => 'Add Tenant Permissions.'}], options[:options], @api_client, {})['account_id']
|
3866
|
+
|
3867
|
+
if !account_id.nil?
|
3868
|
+
accounts << account_id
|
3869
|
+
available_accounts = available_accounts.reject {|it| it['value'] == account_id}
|
3870
|
+
|
3871
|
+
while !available_accounts.empty? && (account_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'account_id', 'type' => 'select', 'fieldLabel' => 'Add Another Tenant', 'selectOptions' => available_accounts, 'required' => false, 'description' => 'Add Tenant Permissions.'}], options[:options], @api_client, {})['account_id'])
|
3872
|
+
if !account_id.nil?
|
3873
|
+
accounts << account_id
|
3874
|
+
available_accounts = available_accounts.reject {|it| it['value'] == account_id}
|
3875
|
+
end
|
3876
|
+
end
|
3877
|
+
end
|
3878
|
+
end
|
3879
|
+
permissions['tenantPermissions'] = {'accounts' => accounts} if !accounts.empty?
|
3880
|
+
end
|
3881
|
+
permissions
|
3882
|
+
end
|
3883
|
+
|
3884
|
+
def update_wiki_page_option_types
|
3885
|
+
[
|
3886
|
+
{'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => false, 'displayOrder' => 1, 'description' => 'The name of the wiki page for this instance. Default is the instance name.'},
|
3887
|
+
#{'fieldName' => 'category', 'fieldLabel' => 'Category', 'type' => 'text', 'required' => false, 'displayOrder' => 2},
|
3888
|
+
{'fieldName' => 'content', 'fieldLabel' => 'Content', 'type' => 'textarea', 'required' => false, 'displayOrder' => 3, 'description' => 'The content (markdown) of the wiki page.'}
|
3889
|
+
]
|
3890
|
+
end
|
3891
|
+
|
3892
|
+
def print_permissions(permissions)
|
3893
|
+
if permissions.nil?
|
3894
|
+
print_h2 "Permissions"
|
3895
|
+
print yellow,"No permissions found.",reset,"\n"
|
3896
|
+
else
|
3897
|
+
if !permissions['resourcePermissions'].nil?
|
3898
|
+
print_h2 "Group Access"
|
3899
|
+
rows = []
|
3900
|
+
if permissions['resourcePermissions']['all']
|
3901
|
+
rows.push({group: 'All'})
|
3902
|
+
end
|
3903
|
+
if permissions['resourcePermissions']['sites']
|
3904
|
+
permissions['resourcePermissions']['sites'].each do |site|
|
3905
|
+
rows.push({group: site['name'], default: site['default'] ? 'Yes' : ''})
|
3906
|
+
end
|
3907
|
+
end
|
3908
|
+
if rows.empty?
|
3909
|
+
print yellow,"No group access",reset,"\n"
|
3910
|
+
else
|
3911
|
+
columns = [:group, :default]
|
3912
|
+
print cyan
|
3913
|
+
print as_pretty_table(rows, columns)
|
3914
|
+
end
|
3915
|
+
|
3916
|
+
print_h2 "Plan Access"
|
3917
|
+
rows = []
|
3918
|
+
if permissions['resourcePermissions']['allPlans']
|
3919
|
+
rows.push({plan: 'All'})
|
3920
|
+
end
|
3921
|
+
if permissions['resourcePermissions']['plans']
|
3922
|
+
permissions['resourcePermissions']['plans'].each do |plan|
|
3923
|
+
rows.push({plan: plan['name'], default: plan['default'] ? 'Yes' : ''})
|
3924
|
+
end
|
3925
|
+
end
|
3926
|
+
if rows.empty?
|
3927
|
+
print yellow,"No plan access",reset,"\n"
|
3928
|
+
else
|
3929
|
+
columns = [:plan, :default]
|
3930
|
+
print cyan
|
3931
|
+
print as_pretty_table(rows, columns)
|
3932
|
+
end
|
3933
|
+
|
3934
|
+
if !permissions['tenantPermissions'].nil?
|
3935
|
+
print_h2 "Tenant Permissions"
|
3936
|
+
if !permissions['resourcePool'].nil?
|
3937
|
+
print cyan
|
3938
|
+
print "Visibility: #{permissions['resourcePool']['visibility'].to_s.capitalize}".center(20)
|
3939
|
+
print "\n"
|
3940
|
+
end
|
3941
|
+
if !permissions['tenantPermissions'].nil?
|
3942
|
+
print cyan
|
3943
|
+
print "Accounts: #{permissions['tenantPermissions']['accounts'].join(', ')}".center(20)
|
3944
|
+
print "\n"
|
3945
|
+
end
|
3946
|
+
end
|
3947
|
+
print "\n"
|
3948
|
+
end
|
3949
|
+
end
|
3950
|
+
end
|
3951
|
+
|
3952
|
+
end
|