morpheus-cli 5.3.0.3 → 5.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Dockerfile +1 -1
- data/README.md +1 -3
- data/lib/morpheus/api/api_client.rb +48 -14
- data/lib/morpheus/api/certificate_types_interface.rb +14 -0
- data/lib/morpheus/api/certificates_interface.rb +9 -0
- data/lib/morpheus/api/integration_types_interface.rb +14 -0
- data/lib/morpheus/api/integrations_interface.rb +7 -22
- data/lib/morpheus/api/network_services_interface.rb +14 -0
- data/lib/morpheus/api/read_interface.rb +23 -0
- data/lib/morpheus/api/rest_interface.rb +12 -10
- data/lib/morpheus/api/roles_interface.rb +7 -0
- data/lib/morpheus/api/servers_interface.rb +7 -0
- data/lib/morpheus/api/user_settings_interface.rb +38 -18
- data/lib/morpheus/api/vdi_allocations_interface.rb +9 -0
- data/lib/morpheus/api/vdi_apps_interface.rb +9 -0
- data/lib/morpheus/api/vdi_gateways_interface.rb +9 -0
- data/lib/morpheus/api/vdi_interface.rb +28 -0
- data/lib/morpheus/api/vdi_pools_interface.rb +19 -0
- data/lib/morpheus/cli.rb +9 -2
- data/lib/morpheus/cli/apps.rb +59 -75
- data/lib/morpheus/cli/catalog_item_types_command.rb +13 -13
- data/lib/morpheus/cli/certificates_command.rb +575 -0
- data/lib/morpheus/cli/cli_command.rb +61 -6
- data/lib/morpheus/cli/clouds.rb +1 -0
- data/lib/morpheus/cli/clusters.rb +1 -1
- data/lib/morpheus/cli/commands/standard/man_command.rb +4 -5
- data/lib/morpheus/cli/hosts.rb +245 -224
- data/lib/morpheus/cli/instances.rb +150 -167
- data/lib/morpheus/cli/integrations_command.rb +588 -41
- data/lib/morpheus/cli/login.rb +7 -0
- data/lib/morpheus/cli/mixins/print_helper.rb +33 -18
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +3 -3
- data/lib/morpheus/cli/mixins/vdi_helper.rb +246 -0
- data/lib/morpheus/cli/network_routers_command.rb +22 -9
- data/lib/morpheus/cli/networks_command.rb +2 -2
- data/lib/morpheus/cli/option_types.rb +34 -33
- data/lib/morpheus/cli/remote.rb +1 -1
- data/lib/morpheus/cli/reports_command.rb +4 -1
- data/lib/morpheus/cli/roles.rb +215 -55
- data/lib/morpheus/cli/subnets_command.rb +11 -2
- data/lib/morpheus/cli/user_settings_command.rb +268 -57
- data/lib/morpheus/cli/vdi_allocations_command.rb +159 -0
- data/lib/morpheus/cli/vdi_apps_command.rb +317 -0
- data/lib/morpheus/cli/vdi_command.rb +359 -0
- data/lib/morpheus/cli/vdi_gateways_command.rb +290 -0
- data/lib/morpheus/cli/vdi_pools_command.rb +571 -0
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/rest_client.rb +30 -0
- data/lib/morpheus/terminal.rb +15 -7
- metadata +18 -2
@@ -0,0 +1,571 @@
|
|
1
|
+
require 'morpheus/cli/cli_command'
|
2
|
+
|
3
|
+
# CLI command VDI Pool management
|
4
|
+
# UI is Tools: VDI Pools
|
5
|
+
# API is /vdi-pools and returns vdiPools
|
6
|
+
class Morpheus::Cli::VdiPoolsCommand
|
7
|
+
include Morpheus::Cli::CliCommand
|
8
|
+
include Morpheus::Cli::VdiHelper
|
9
|
+
include Morpheus::Cli::ProvisioningHelper
|
10
|
+
include Morpheus::Cli::OptionSourceHelper
|
11
|
+
|
12
|
+
set_command_name :'vdi-pools'
|
13
|
+
set_command_description "View and manage VDI pools"
|
14
|
+
|
15
|
+
register_subcommands :list, :get, :add, :update, :remove
|
16
|
+
|
17
|
+
def connect(opts)
|
18
|
+
@api_client = establish_remote_appliance_connection(opts)
|
19
|
+
@vdi_pools_interface = @api_client.vdi_pools
|
20
|
+
@vdi_allocations_interface = @api_client.vdi_allocations
|
21
|
+
@vdi_apps_interface = @api_client.vdi_apps
|
22
|
+
@vdi_gateways_interface = @api_client.vdi_gateways
|
23
|
+
@option_types_interface = @api_client.option_types
|
24
|
+
end
|
25
|
+
|
26
|
+
def handle(args)
|
27
|
+
handle_subcommand(args)
|
28
|
+
end
|
29
|
+
|
30
|
+
def list(args)
|
31
|
+
options = {}
|
32
|
+
params = {}
|
33
|
+
ref_ids = []
|
34
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
35
|
+
opts.banner = subcommand_usage("[search]")
|
36
|
+
opts.on( '--enabled [on|off]', String, "Filter by enabled" ) do |val|
|
37
|
+
params['enabled'] = (val.to_s != 'false' && val.to_s != 'off')
|
38
|
+
end
|
39
|
+
build_standard_list_options(opts, options)
|
40
|
+
opts.footer = "List VDI pools."
|
41
|
+
end
|
42
|
+
optparse.parse!(args)
|
43
|
+
connect(options)
|
44
|
+
if args.count > 0
|
45
|
+
options[:phrase] = args.join(" ")
|
46
|
+
end
|
47
|
+
params.merge!(parse_list_options(options))
|
48
|
+
@vdi_pools_interface.setopts(options)
|
49
|
+
if options[:dry_run]
|
50
|
+
print_dry_run @vdi_pools_interface.dry.list(params)
|
51
|
+
return
|
52
|
+
end
|
53
|
+
json_response = @vdi_pools_interface.list(params)
|
54
|
+
render_response(json_response, options, vdi_pool_list_key) do
|
55
|
+
vdi_pools = json_response[vdi_pool_list_key]
|
56
|
+
print_h1 "Morpheus VDI Pools", parse_list_subtitles(options), options
|
57
|
+
if vdi_pools.empty?
|
58
|
+
print cyan,"No VDI pools found.",reset,"\n"
|
59
|
+
else
|
60
|
+
print as_pretty_table(vdi_pools, vdi_pool_list_column_definitions.upcase_keys!, options)
|
61
|
+
print_results_pagination(json_response)
|
62
|
+
end
|
63
|
+
print reset,"\n"
|
64
|
+
end
|
65
|
+
return 0, nil
|
66
|
+
end
|
67
|
+
|
68
|
+
def get(args)
|
69
|
+
params = {}
|
70
|
+
options = {}
|
71
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
72
|
+
opts.banner = subcommand_usage("[pool]")
|
73
|
+
opts.on( '-c', '--config', "Display raw config only. Default is YAML. Combine with -j for JSON instead." ) do
|
74
|
+
options[:show_config] = true
|
75
|
+
end
|
76
|
+
opts.on('--no-config', "Do not display Config YAML." ) do
|
77
|
+
options[:no_config] = true
|
78
|
+
end
|
79
|
+
build_standard_get_options(opts, options)
|
80
|
+
opts.footer = <<-EOT
|
81
|
+
Get details about a specific VDI pool.
|
82
|
+
[pool] is required. This is the name or id of a VDI pool.
|
83
|
+
EOT
|
84
|
+
end
|
85
|
+
optparse.parse!(args)
|
86
|
+
verify_args!(args:args, optparse:optparse, min:1)
|
87
|
+
connect(options)
|
88
|
+
params.merge!(parse_query_options(options))
|
89
|
+
id_list = parse_id_list(args)
|
90
|
+
return run_command_for_each_arg(id_list) do |arg|
|
91
|
+
_get(arg, params, options)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def _get(id, params, options)
|
96
|
+
vdi_pool = nil
|
97
|
+
if id.to_s !~ /\A\d{1,}\Z/
|
98
|
+
vdi_pool = find_vdi_pool_by_name(id)
|
99
|
+
return 1, "VDI pool not found for #{id}" if vdi_pool.nil?
|
100
|
+
id = vdi_pool['id']
|
101
|
+
end
|
102
|
+
@vdi_pools_interface.setopts(options)
|
103
|
+
if options[:dry_run]
|
104
|
+
print_dry_run @vdi_pools_interface.dry.get(id, params)
|
105
|
+
return
|
106
|
+
end
|
107
|
+
json_response = @vdi_pools_interface.get(id, params)
|
108
|
+
vdi_pool = json_response[vdi_pool_object_key]
|
109
|
+
config = vdi_pool['config'] || {}
|
110
|
+
# export just the config as json or yaml (default)
|
111
|
+
if options[:show_config]
|
112
|
+
unless options[:json] || options[:yaml] || options[:csv]
|
113
|
+
options[:yaml] = true
|
114
|
+
end
|
115
|
+
return render_with_format(config, options)
|
116
|
+
end
|
117
|
+
render_response(json_response, options, vdi_pool_object_key) do
|
118
|
+
print_h1 "VDI Pool Details", [], options
|
119
|
+
print cyan
|
120
|
+
show_columns = vdi_pool_column_definitions
|
121
|
+
show_columns.delete("VDI Apps") unless vdi_pool['apps']
|
122
|
+
show_columns.delete("VDI Gateway") unless vdi_pool['gateway']
|
123
|
+
show_columns.delete("Guest Console Jump Host") unless vdi_pool['guestConsoleJumpHost']
|
124
|
+
show_columns.delete("Guest Console Jump Port") unless vdi_pool['guestConsoleJumpPort']
|
125
|
+
show_columns.delete("Guest Console Jump Username") unless vdi_pool['guestConsoleJumpUsername']
|
126
|
+
show_columns.delete("Guest Console Jump Password") unless vdi_pool['guestConsoleJumpPassword']
|
127
|
+
show_columns.delete("Guest Console Jump Keypair") unless vdi_pool['guestConsoleJumpKeypair']
|
128
|
+
print_description_list(show_columns, vdi_pool)
|
129
|
+
|
130
|
+
# need to make another query if we want to show these here:
|
131
|
+
# allocations = @allocations.list({poolId:vdi_pool['id']})['vdiAllocations']
|
132
|
+
# if vdi_pool['allocations'] && vdi_pool['allocations'].size > 0
|
133
|
+
# print_h2 "Allocations"
|
134
|
+
# opt_columns = [
|
135
|
+
# {"ID" => lambda {|it| it['id'] } },
|
136
|
+
# {"USER" => lambda {|it| it['user'] ? it['user']['username'] : nil } },
|
137
|
+
# {"STATUS" => lambda {|it| format_vdi_allocation_status(it) } },
|
138
|
+
# {"CREATED" => lambda {|it| format_local_dt it['dateCreated'] } },
|
139
|
+
# {"RELEASE DATE" => lambda {|it| format_local_dt it['releaseDate'] } },
|
140
|
+
# ]
|
141
|
+
# print as_pretty_table(vdi_pool['allocations'], opt_columns)
|
142
|
+
# else
|
143
|
+
# # print cyan,"No option types found for this VDI pool.","\n",reset
|
144
|
+
# end
|
145
|
+
|
146
|
+
if options[:no_config] != true
|
147
|
+
print_h2 "Config YAML"
|
148
|
+
if config
|
149
|
+
#print reset,(JSON.pretty_generate(config) rescue config),"\n",reset
|
150
|
+
#print reset,(as_yaml(config, options) rescue config),"\n",reset
|
151
|
+
config_string = as_yaml(config, options) rescue config
|
152
|
+
config_lines = config_string.split("\n")
|
153
|
+
config_line_count = config_lines.size
|
154
|
+
max_lines = 10
|
155
|
+
if config_lines.size > max_lines
|
156
|
+
config_string = config_lines.first(max_lines).join("\n")
|
157
|
+
config_string << "\n\n"
|
158
|
+
config_string << "#{dark}(#{(config_line_count - max_lines)} more lines were not shown, use -c to show the config)#{reset}"
|
159
|
+
#config_string << "\n"
|
160
|
+
end
|
161
|
+
# strip --- yaml header
|
162
|
+
if config_string[0..3] == "---\n"
|
163
|
+
config_string = config_string[4..-1]
|
164
|
+
end
|
165
|
+
print reset,config_string.chomp("\n"),"\n",reset
|
166
|
+
else
|
167
|
+
print reset,"(blank)","\n",reset
|
168
|
+
end
|
169
|
+
|
170
|
+
end
|
171
|
+
|
172
|
+
print reset,"\n"
|
173
|
+
end
|
174
|
+
return 0, nil
|
175
|
+
end
|
176
|
+
|
177
|
+
def add(args)
|
178
|
+
options = {}
|
179
|
+
params = {}
|
180
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
181
|
+
opts.banner = subcommand_usage("[name] [options]")
|
182
|
+
build_option_type_options(opts, options, add_vdi_pool_option_types)
|
183
|
+
opts.on('--config-file FILE', String, "Config from a local JSON or YAML file") do |val|
|
184
|
+
options[:config_file] = val.to_s
|
185
|
+
file_content = nil
|
186
|
+
full_filename = File.expand_path(options[:config_file])
|
187
|
+
if File.exists?(full_filename)
|
188
|
+
file_content = File.read(full_filename)
|
189
|
+
else
|
190
|
+
print_red_alert "File not found: #{full_filename}"
|
191
|
+
return 1
|
192
|
+
end
|
193
|
+
parse_result = parse_json_or_yaml(file_content)
|
194
|
+
config_map = parse_result[:data]
|
195
|
+
if config_map.nil?
|
196
|
+
# todo: bubble up JSON.parse error message
|
197
|
+
raise_command_error "Failed to parse config as YAML or JSON. Error: #{parse_result[:err]}"
|
198
|
+
#raise_command_error "Failed to parse config as valid YAML or JSON."
|
199
|
+
else
|
200
|
+
params['config'] = config_map
|
201
|
+
options[:options]['config'] = params['config'] # or file_content
|
202
|
+
end
|
203
|
+
end
|
204
|
+
opts.on( '-i', '--interactive', "Interactive Config, prompt for each input of the instance configuration" ) do
|
205
|
+
options[:interactive_config] = true
|
206
|
+
end
|
207
|
+
build_standard_add_options(opts, options)
|
208
|
+
opts.footer = <<-EOT
|
209
|
+
Create a new VDI pool.
|
210
|
+
EOT
|
211
|
+
end
|
212
|
+
optparse.parse!(args)
|
213
|
+
verify_args!(args:args, optparse:optparse, min:0, max:1)
|
214
|
+
options[:options]['name'] = args[0] if args[0]
|
215
|
+
if options[:options]['logo']
|
216
|
+
options[:options]['iconPath'] = 'custom'
|
217
|
+
end
|
218
|
+
connect(options)
|
219
|
+
payload = {}
|
220
|
+
if options[:payload]
|
221
|
+
payload = options[:payload]
|
222
|
+
payload.deep_merge!({vdi_pool_object_key => parse_passed_options(options)})
|
223
|
+
else
|
224
|
+
params.deep_merge!(parse_passed_options(options))
|
225
|
+
# prompt for option types
|
226
|
+
# skip config if using interactive prompt
|
227
|
+
filtered_option_types = add_vdi_pool_option_types
|
228
|
+
if options[:interactive_config] || options[:options]['instanceConfig']
|
229
|
+
filtered_option_types = filtered_option_types.reject {|it| it['fieldName'] == 'config' }
|
230
|
+
end
|
231
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt(filtered_option_types, options[:options], @api_client, options[:params])
|
232
|
+
params.deep_merge!(v_prompt)
|
233
|
+
# convert checkbox "on" and "off" to true and false
|
234
|
+
params.booleanize!
|
235
|
+
# logo upload requires multipart instead of json
|
236
|
+
if params['logo']
|
237
|
+
params['logo'] = File.new(File.expand_path(params['logo']), 'rb')
|
238
|
+
payload[:multipart] = true
|
239
|
+
end
|
240
|
+
# convert config string to a map
|
241
|
+
config = params['config']
|
242
|
+
if config && config.is_a?(String)
|
243
|
+
parse_result = parse_json_or_yaml(config)
|
244
|
+
config_map = parse_result[:data]
|
245
|
+
if config_map.nil?
|
246
|
+
# todo: bubble up JSON.parse error message
|
247
|
+
raise_command_error "Failed to parse config as YAML or JSON. Error: #{parse_result[:err]}"
|
248
|
+
#raise_command_error "Failed to parse config as valid YAML or JSON."
|
249
|
+
else
|
250
|
+
params['config'] = config_map
|
251
|
+
end
|
252
|
+
end
|
253
|
+
# pass instanceConfig: "{...}" instead of config: {} to preserve config order...
|
254
|
+
if params['config']
|
255
|
+
config_map = params.delete('config')
|
256
|
+
params['instanceConfig'] = as_json(config_map, {:pretty_json => true})
|
257
|
+
end
|
258
|
+
if options[:interactive_config]
|
259
|
+
print_h2 "Instance Config"
|
260
|
+
config_map = prompt_vdi_config(options)
|
261
|
+
params['config'] = config_map
|
262
|
+
end
|
263
|
+
# massage association params a bit
|
264
|
+
params['gateway'] = {'id' => params['gateway']} if params['gateway'] && !params['gateway'].is_a?(Hash)
|
265
|
+
# params['apps'] = ...
|
266
|
+
payload.deep_merge!({vdi_pool_object_key => params})
|
267
|
+
end
|
268
|
+
@vdi_pools_interface.setopts(options)
|
269
|
+
if options[:dry_run]
|
270
|
+
print_dry_run @vdi_pools_interface.dry.create(payload)
|
271
|
+
return 0, nil
|
272
|
+
end
|
273
|
+
json_response = @vdi_pools_interface.create(payload)
|
274
|
+
vdi_pool = json_response[vdi_pool_object_key]
|
275
|
+
render_response(json_response, options, vdi_pool_object_key) do
|
276
|
+
print_green_success "Added VDI pool #{vdi_pool['name']}"
|
277
|
+
return _get(vdi_pool["id"], {}, options)
|
278
|
+
end
|
279
|
+
return 0, nil
|
280
|
+
end
|
281
|
+
|
282
|
+
def update(args)
|
283
|
+
options = {}
|
284
|
+
params = {}
|
285
|
+
payload = {}
|
286
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
287
|
+
opts.banner = subcommand_usage("[pool] [options]")
|
288
|
+
build_option_type_options(opts, options, update_vdi_pool_option_types)
|
289
|
+
opts.on('--config-file FILE', String, "Config from a local JSON or YAML file") do |val|
|
290
|
+
options[:config_file] = val.to_s
|
291
|
+
file_content = nil
|
292
|
+
full_filename = File.expand_path(options[:config_file])
|
293
|
+
if File.exists?(full_filename)
|
294
|
+
file_content = File.read(full_filename)
|
295
|
+
else
|
296
|
+
print_red_alert "File not found: #{full_filename}"
|
297
|
+
return 1
|
298
|
+
end
|
299
|
+
parse_result = parse_json_or_yaml(file_content)
|
300
|
+
config_map = parse_result[:data]
|
301
|
+
if config_map.nil?
|
302
|
+
# todo: bubble up JSON.parse error message
|
303
|
+
raise_command_error "Failed to parse config as YAML or JSON. Error: #{parse_result[:err]}"
|
304
|
+
#raise_command_error "Failed to parse config as valid YAML or JSON."
|
305
|
+
else
|
306
|
+
params['config'] = config_map
|
307
|
+
options[:options]['config'] = params['config'] # or file_content
|
308
|
+
end
|
309
|
+
end
|
310
|
+
build_standard_update_options(opts, options)
|
311
|
+
opts.footer = <<-EOT
|
312
|
+
Update a VDI pool.
|
313
|
+
[pool] is required. This is the name or id of a VDI pool.
|
314
|
+
EOT
|
315
|
+
end
|
316
|
+
optparse.parse!(args)
|
317
|
+
verify_args!(args:args, optparse:optparse, count:1)
|
318
|
+
if options[:options]['logo']
|
319
|
+
options[:options]['iconPath'] = 'custom'
|
320
|
+
end
|
321
|
+
connect(options)
|
322
|
+
vdi_pool = find_vdi_pool_by_name_or_id(args[0])
|
323
|
+
return 1 if vdi_pool.nil?
|
324
|
+
payload = {}
|
325
|
+
if options[:payload]
|
326
|
+
payload = options[:payload]
|
327
|
+
payload.deep_merge!({vdi_pool_object_key => parse_passed_options(options)})
|
328
|
+
else
|
329
|
+
params.deep_merge!(parse_passed_options(options))
|
330
|
+
# do not prompt on update
|
331
|
+
v_prompt = Morpheus::Cli::OptionTypes.no_prompt(update_vdi_pool_option_types, options[:options], @api_client, options[:params])
|
332
|
+
v_prompt.deep_compact!
|
333
|
+
params.deep_merge!(v_prompt)
|
334
|
+
# convert checkbox "on" and "off" to true and false
|
335
|
+
params.booleanize!
|
336
|
+
# logo upload requires multipart instead of json
|
337
|
+
if params['logo']
|
338
|
+
params['logo'] = File.new(File.expand_path(params['logo']), 'rb')
|
339
|
+
payload[:multipart] = true
|
340
|
+
end
|
341
|
+
# convert config string to a map
|
342
|
+
config = params['config']
|
343
|
+
if config && config.is_a?(String)
|
344
|
+
parse_result = parse_json_or_yaml(config)
|
345
|
+
config_map = parse_result[:data]
|
346
|
+
if config_map.nil?
|
347
|
+
# todo: bubble up JSON.parse error message
|
348
|
+
raise_command_error "Failed to parse config as YAML or JSON. Error: #{parse_result[:err]}"
|
349
|
+
#raise_command_error "Failed to parse config as valid YAML or JSON."
|
350
|
+
else
|
351
|
+
params['config'] = config_map
|
352
|
+
end
|
353
|
+
end
|
354
|
+
# pass instanceConfig: "{...}" instead of config: {} to preserve config order...
|
355
|
+
if params['config']
|
356
|
+
config_map = params.delete('config')
|
357
|
+
params['instanceConfig'] = as_json(config_map, {:pretty_json => true})
|
358
|
+
end
|
359
|
+
# massage association params a bit
|
360
|
+
params['gateway'] = {'id' => params['gateway']} if params['gateway'] && !params['gateway'].is_a?(Hash)
|
361
|
+
# params['apps'] = ...
|
362
|
+
payload.deep_merge!({vdi_pool_object_key => params})
|
363
|
+
if payload[vdi_pool_object_key].empty? # || options[:no_prompt]
|
364
|
+
raise_command_error "Specify at least one option to update.\n#{optparse}"
|
365
|
+
end
|
366
|
+
end
|
367
|
+
@vdi_pools_interface.setopts(options)
|
368
|
+
if options[:dry_run]
|
369
|
+
print_dry_run @vdi_pools_interface.dry.update(vdi_pool['id'], payload)
|
370
|
+
return
|
371
|
+
end
|
372
|
+
json_response = @vdi_pools_interface.update(vdi_pool['id'], payload)
|
373
|
+
vdi_pool = json_response[vdi_pool_object_key]
|
374
|
+
render_response(json_response, options, vdi_pool_object_key) do
|
375
|
+
print_green_success "Updated VDI pool #{vdi_pool['name']}"
|
376
|
+
return _get(vdi_pool["id"], {}, options)
|
377
|
+
end
|
378
|
+
return 0, nil
|
379
|
+
end
|
380
|
+
|
381
|
+
def remove(args)
|
382
|
+
options = {}
|
383
|
+
params = {}
|
384
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
385
|
+
opts.banner = subcommand_usage("[pool] [options]")
|
386
|
+
build_standard_remove_options(opts, options)
|
387
|
+
opts.footer = <<-EOT
|
388
|
+
Delete a VDI pool.
|
389
|
+
[pool] is required. This is the name or id of a VDI pool.
|
390
|
+
EOT
|
391
|
+
end
|
392
|
+
optparse.parse!(args)
|
393
|
+
verify_args!(args:args, optparse:optparse, count:1)
|
394
|
+
connect(options)
|
395
|
+
vdi_pool = find_vdi_pool_by_name_or_id(args[0])
|
396
|
+
return 1 if vdi_pool.nil?
|
397
|
+
@vdi_pools_interface.setopts(options)
|
398
|
+
if options[:dry_run]
|
399
|
+
print_dry_run @vdi_pools_interface.dry.destroy(vdi_pool['id'], params)
|
400
|
+
return
|
401
|
+
end
|
402
|
+
unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete the VDI pool #{vdi_pool['name']}?")
|
403
|
+
return 9, "aborted command"
|
404
|
+
end
|
405
|
+
json_response = @vdi_pools_interface.destroy(vdi_pool['id'], params)
|
406
|
+
render_response(json_response, options) do
|
407
|
+
print_green_success "Removed VDI pool #{vdi_pool['name']}"
|
408
|
+
end
|
409
|
+
return 0, nil
|
410
|
+
end
|
411
|
+
|
412
|
+
private
|
413
|
+
|
414
|
+
def vdi_pool_list_column_definitions()
|
415
|
+
|
416
|
+
{
|
417
|
+
"ID" => 'id',
|
418
|
+
"Name" => 'name',
|
419
|
+
"Description" => 'description',
|
420
|
+
"Persistent" => lambda {|it| format_boolean(it['persistentUser']) },
|
421
|
+
"Enabled" => lambda {|it| format_boolean(it['enabled']) },
|
422
|
+
"Pool Usage" => lambda {|it|
|
423
|
+
# todo: [== ] 2/8 would be neat generate_usage_bar(...)
|
424
|
+
|
425
|
+
# used_value = it['reservedCount'] ? format_number(it['reservedCount']) : "N/A"
|
426
|
+
# max_value = it['maxPoolSize'] ? format_number(it['maxPoolSize']) : "N/A"
|
427
|
+
used_value = it['usedCount']
|
428
|
+
max_value = it['maxPoolSize']
|
429
|
+
usage_bar = generate_usage_bar(used_value, max_value, {:bar_color => cyan, :max_bars => 10, :percent_sigdig => 0})
|
430
|
+
usage_label = "#{used_value} / #{max_value}"
|
431
|
+
usage_bar + cyan + " " + "(" + usage_label + ")"
|
432
|
+
},
|
433
|
+
# Status will always show AVAILABLE right now, so that's weird..
|
434
|
+
# "Status" => lambda {|it| format_vdi_pool_status(it) },
|
435
|
+
}
|
436
|
+
end
|
437
|
+
|
438
|
+
def vdi_pool_column_definitions()
|
439
|
+
{
|
440
|
+
"ID" => 'id',
|
441
|
+
"Name" => 'name',
|
442
|
+
"Description" => 'description',
|
443
|
+
"Owner" => lambda {|it| it['owner'] ? it['owner']['username'] || it['owner']['name'] : (it['user'] ? it['user']['username'] || it['user']['name'] : nil) },
|
444
|
+
"Min Idle" => lambda {|it| format_number(it['minIdle']) rescue '' },
|
445
|
+
"Initial Pool Size" => lambda {|it| format_number(it['initialPoolSize']) rescue '' },
|
446
|
+
"Max Idle" => lambda {|it| format_number(it['maxIdle']) rescue '' },
|
447
|
+
"Max Size" => lambda {|it| format_number(it['maxPoolSize']) rescue '' },
|
448
|
+
"Lease Timeout" => lambda {|it| format_number(it['allocationTimeoutMinutes']) rescue '' },
|
449
|
+
"Persistent" => lambda {|it| format_boolean(it['persistentUser']) },
|
450
|
+
"Allow Copy" => lambda {|it| format_boolean(it['allowCopy']) },
|
451
|
+
"Allow Printer" => lambda {|it| format_boolean(it['allowPrinter']) },
|
452
|
+
"Allow File Share" => lambda {|it| format_boolean(it['allowFileshare']) },
|
453
|
+
"Allow Hypervisor Console" => lambda {|it| format_boolean(it['allowHypervisorConsole']) },
|
454
|
+
"Auto Create User" => lambda {|it| format_boolean(it['autoCreateLocalUserOnReservation']) },
|
455
|
+
"Enabled" => lambda {|it| format_boolean(it['enabled']) },
|
456
|
+
"Logo" => lambda {|it| it['logo'] || it['imagePath'] },
|
457
|
+
#"Config" => lambda {|it| it['config'] },
|
458
|
+
"Group" => lambda {|it| it['group'] ? it['group']['name'] : nil },
|
459
|
+
"Cloud" => lambda {|it| it['cloud'] ? it['cloud']['name'] : nil },
|
460
|
+
"VDI Apps" => lambda {|it| it['apps'] ? it['apps'].collect {|vdi_app| vdi_app['name'] }.join(', ') : nil },
|
461
|
+
"VDI Gateway" => lambda {|it| it['gateway'] ? it['gateway']['name'] : nil },
|
462
|
+
"Guest Console Jump Host" => lambda {|it| it['guestConsoleJumpHost'] },
|
463
|
+
"Guest Console Jump Port" => lambda {|it| it['guestConsoleJumpPort'] },
|
464
|
+
"Guest Console Jump Username" => lambda {|it| it['guestConsoleJumpUsername'] },
|
465
|
+
"Guest Console Jump Password" => lambda {|it| it['guestConsoleJumpPassword'] },
|
466
|
+
"Guest Console Jump Keypair" => lambda {|it| it['guestConsoleJumpKeypair'] ? it['guestConsoleJumpKeypair']['name'] : nil },
|
467
|
+
"Idle Count" => lambda {|it| format_number(it['idleCount']) rescue '' },
|
468
|
+
"Reserved Count" => lambda {|it| format_number(it['reservedCount']) rescue '' },
|
469
|
+
"Preparing Count" => lambda {|it| format_number(it['preparingCount']) rescue '' },
|
470
|
+
# "Status" => lambda {|it| format_vdi_pool_status(it) },
|
471
|
+
"Created" => lambda {|it| format_local_dt(it['dateCreated']) },
|
472
|
+
"Updated" => lambda {|it| format_local_dt(it['lastUpdated']) },
|
473
|
+
}
|
474
|
+
end
|
475
|
+
|
476
|
+
def add_vdi_pool_option_types
|
477
|
+
[
|
478
|
+
{'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'description' => 'Choose a unique name for the VDI Pool'},
|
479
|
+
{'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text', 'description' => 'Description'},
|
480
|
+
{'fieldName' => 'owner', 'fieldLabel' => 'Owner', 'type' => 'select', 'optionSource' => 'users'},
|
481
|
+
{'fieldName' => 'minIdle', 'fieldLabel' => 'Min Idle', 'type' => 'number', 'defaultValue' => '0', 'description' => 'Sets the minimum number of idle instances on standby in the pool. The pool will always try to maintain this number of available instances on standby.'},
|
482
|
+
{'fieldName' => 'initialPoolSize', 'fieldLabel' => 'Initial Pool Size', 'type' => 'number', 'defaultValue' => '0', 'description' => 'The initial size of the pool to be allocated on creation.'},
|
483
|
+
{'fieldName' => 'maxIdle', 'fieldLabel' => 'Max Idle', 'type' => 'number', 'defaultValue' => '0', 'description' => 'Sets the maximum number of idle instances on standby in the pool. If the number of idle instances supersedes this, the pool will start removing instances.'},
|
484
|
+
{'fieldName' => 'maxPoolSize', 'fieldLabel' => 'Max Size', 'type' => 'number', 'required' => true, 'description' => 'Max limit on number of allocations and instances within the pool.'},
|
485
|
+
{'fieldName' => 'allocationTimeoutMinutes', 'fieldLabel' => 'Lease Timeout', 'type' => 'number', 'description' => 'Time (in minutes) after a user disconnects before an allocation is recycled or shutdown depending on persistence.'},
|
486
|
+
{'fieldName' => 'persistentUser', 'fieldLabel' => 'Persistent', 'type' => 'checkbox', 'defaultValue' => false},
|
487
|
+
{'fieldName' => 'allowCopy', 'fieldLabel' => 'Allow Copy', 'type' => 'checkbox', 'defaultValue' => false},
|
488
|
+
{'fieldName' => 'allowPrinter', 'fieldLabel' => 'Allow Printer', 'type' => 'checkbox', 'defaultValue' => false},
|
489
|
+
{'fieldName' => 'allowFileshare', 'fieldLabel' => 'Allow File Share', 'type' => 'checkbox', 'defaultValue' => false},
|
490
|
+
{'fieldName' => 'allowHypervisorConsole', 'fieldLabel' => 'Allow Hypervisor Console', 'type' => 'checkbox', 'defaultValue' => false},
|
491
|
+
{'fieldName' => 'autoCreateLocalUserOnReservation', 'fieldLabel' => 'Auto Create User', 'type' => 'checkbox', 'defaultValue' => false},
|
492
|
+
{'fieldName' => 'enabled', 'fieldLabel' => 'Enabled', 'type' => 'checkbox', 'defaultValue' => true, 'description' => 'Enable the VDI Pool to make it available for allocation.'},
|
493
|
+
#{'fieldName' => 'iconPath', 'fieldLabel' => 'Logo', 'type' => 'select', 'optionSource' => 'iconList', 'defaultValue' => 'resource'},
|
494
|
+
# iconList does not include custom, so add it ourselves..
|
495
|
+
# only prompt for logo file if custom
|
496
|
+
{'code' => 'vdiPool.iconPath', 'fieldName' => 'iconPath', 'fieldLabel' => 'Logo', 'type' => 'select', 'optionSource' => lambda { |api_client, api_params|
|
497
|
+
dropdown = api_client.options.options_for_source("iconList")['data']
|
498
|
+
if !dropdown.find {|it| it['value'] == 'custom'}
|
499
|
+
dropdown.push({'name' => 'Custom', 'value' => 'custom'})
|
500
|
+
end
|
501
|
+
dropdown
|
502
|
+
}, 'description' => 'Logo icon path or custom if uploading a custom logo', 'defaultValue' => 'resource'},
|
503
|
+
{'dependsOnCode' => 'vdiPool.iconPath:custom', 'fieldName' => 'logo', 'fieldLabel' => 'Logo File', 'type' => 'file', 'required' => true, 'description' => 'Local filepath of image file to upload as custom icon'},
|
504
|
+
# {'fieldName' => 'apps', 'fieldLabel' => 'VDI Apps', 'type' => 'multiSelect', 'description' => 'VDI Apps, comma separated list of names or IDs.'},
|
505
|
+
# {'fieldName' => 'gateway', 'fieldLabel' => 'Gateway', 'type' => 'select', 'optionSource' => 'vdiGateways'},
|
506
|
+
{'fieldName' => 'apps', 'fieldLabel' => 'VDI Apps', 'type' => 'multiSelect', 'optionSource' => lambda { |api_client, api_params|
|
507
|
+
api_client.vdi_apps.list({max:10000})[vdi_app_list_key].collect {|it| {'name' => it['name'], 'value' => it['id']} }
|
508
|
+
}, 'description' => 'VDI Apps, comma separated list of names or IDs.'},
|
509
|
+
{'fieldName' => 'gateway', 'fieldLabel' => 'VDI Gateway', 'type' => 'select', 'optionSource' => lambda { |api_client, api_params|
|
510
|
+
api_client.vdi_gateways.list({max:10000})[vdi_gateway_list_key].collect {|it| {'name' => it['name'], 'value' => it['id']} }
|
511
|
+
}, 'description' => 'VDI Gateway'},
|
512
|
+
{'fieldName' => 'config', 'fieldLabel' => 'Config', 'type' => 'code-editor', 'description' => 'JSON or YAML', 'required' => true},
|
513
|
+
# todo:
|
514
|
+
# Guest Console Jump Host
|
515
|
+
# Guest Console Jump Port
|
516
|
+
# Guest Console Jump Username
|
517
|
+
# Guest Console Jump Password
|
518
|
+
# Guest Console Jump Keypair
|
519
|
+
]
|
520
|
+
end
|
521
|
+
|
522
|
+
def update_vdi_pool_option_types
|
523
|
+
list = add_vdi_pool_option_types.collect {|it|
|
524
|
+
it.delete('required')
|
525
|
+
it.delete('defaultValue')
|
526
|
+
it
|
527
|
+
}
|
528
|
+
list = list.reject {|it| ["type"].include? it['fieldName'] }
|
529
|
+
list
|
530
|
+
end
|
531
|
+
|
532
|
+
# finders are in VdiHelper mixin
|
533
|
+
|
534
|
+
# prompt for an instance config (vdiPool.instanceConfig)
|
535
|
+
def prompt_vdi_config(options)
|
536
|
+
# use config if user passed one in..
|
537
|
+
scope_context = 'instanceConfig'
|
538
|
+
scoped_instance_config = {}
|
539
|
+
if options[:options][scope_context].is_a?(Hash)
|
540
|
+
scoped_instance_config = options[:options][scope_context]
|
541
|
+
end
|
542
|
+
|
543
|
+
# now configure an instance like normal, use the config as default options with :always_prompt
|
544
|
+
instance_prompt_options = {}
|
545
|
+
# instance_prompt_options[:group] = group ? group['id'] : nil
|
546
|
+
# #instance_prompt_options[:cloud] = cloud ? cloud['name'] : nil
|
547
|
+
# instance_prompt_options[:default_cloud] = cloud ? cloud['name'] : nil
|
548
|
+
# instance_prompt_options[:environment] = selected_environment ? selected_environment['code'] : nil
|
549
|
+
# instance_prompt_options[:default_security_groups] = scoped_instance_config['securityGroups'] ? scoped_instance_config['securityGroups'] : nil
|
550
|
+
|
551
|
+
instance_prompt_options[:no_prompt] = options[:no_prompt]
|
552
|
+
#instance_prompt_options[:always_prompt] = options[:no_prompt] != true # options[:always_prompt]
|
553
|
+
instance_prompt_options[:options] = scoped_instance_config
|
554
|
+
#instance_prompt_options[:options][:always_prompt] = instance_prompt_options[:no_prompt] != true
|
555
|
+
instance_prompt_options[:options][:no_prompt] = instance_prompt_options[:no_prompt]
|
556
|
+
|
557
|
+
#instance_prompt_options[:name_required] = true
|
558
|
+
# instance_prompt_options[:instance_type_code] = instance_type_code
|
559
|
+
# todo: an effort to render more useful help eg. -O Web.0.instance.name
|
560
|
+
help_field_prefix = scope_context
|
561
|
+
instance_prompt_options[:help_field_prefix] = help_field_prefix
|
562
|
+
instance_prompt_options[:options][:help_field_prefix] = help_field_prefix
|
563
|
+
# instance_prompt_options[:locked_fields] = scoped_instance_config['lockedFields']
|
564
|
+
# instance_prompt_options[:for_app] = true
|
565
|
+
instance_prompt_options[:select_datastore] = true
|
566
|
+
instance_prompt_options[:name_required] = true
|
567
|
+
# this provisioning helper method handles all (most) of the parsing and prompting
|
568
|
+
instance_config_payload = prompt_new_instance(instance_prompt_options)
|
569
|
+
return instance_config_payload
|
570
|
+
end
|
571
|
+
end
|