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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/README.md +1 -3
  4. data/lib/morpheus/api/api_client.rb +48 -14
  5. data/lib/morpheus/api/certificate_types_interface.rb +14 -0
  6. data/lib/morpheus/api/certificates_interface.rb +9 -0
  7. data/lib/morpheus/api/integration_types_interface.rb +14 -0
  8. data/lib/morpheus/api/integrations_interface.rb +7 -22
  9. data/lib/morpheus/api/network_services_interface.rb +14 -0
  10. data/lib/morpheus/api/read_interface.rb +23 -0
  11. data/lib/morpheus/api/rest_interface.rb +12 -10
  12. data/lib/morpheus/api/roles_interface.rb +7 -0
  13. data/lib/morpheus/api/servers_interface.rb +7 -0
  14. data/lib/morpheus/api/user_settings_interface.rb +38 -18
  15. data/lib/morpheus/api/vdi_allocations_interface.rb +9 -0
  16. data/lib/morpheus/api/vdi_apps_interface.rb +9 -0
  17. data/lib/morpheus/api/vdi_gateways_interface.rb +9 -0
  18. data/lib/morpheus/api/vdi_interface.rb +28 -0
  19. data/lib/morpheus/api/vdi_pools_interface.rb +19 -0
  20. data/lib/morpheus/cli.rb +9 -2
  21. data/lib/morpheus/cli/apps.rb +59 -75
  22. data/lib/morpheus/cli/catalog_item_types_command.rb +13 -13
  23. data/lib/morpheus/cli/certificates_command.rb +575 -0
  24. data/lib/morpheus/cli/cli_command.rb +61 -6
  25. data/lib/morpheus/cli/clouds.rb +1 -0
  26. data/lib/morpheus/cli/clusters.rb +1 -1
  27. data/lib/morpheus/cli/commands/standard/man_command.rb +4 -5
  28. data/lib/morpheus/cli/hosts.rb +245 -224
  29. data/lib/morpheus/cli/instances.rb +150 -167
  30. data/lib/morpheus/cli/integrations_command.rb +588 -41
  31. data/lib/morpheus/cli/login.rb +7 -0
  32. data/lib/morpheus/cli/mixins/print_helper.rb +33 -18
  33. data/lib/morpheus/cli/mixins/provisioning_helper.rb +3 -3
  34. data/lib/morpheus/cli/mixins/vdi_helper.rb +246 -0
  35. data/lib/morpheus/cli/network_routers_command.rb +22 -9
  36. data/lib/morpheus/cli/networks_command.rb +2 -2
  37. data/lib/morpheus/cli/option_types.rb +34 -33
  38. data/lib/morpheus/cli/remote.rb +1 -1
  39. data/lib/morpheus/cli/reports_command.rb +4 -1
  40. data/lib/morpheus/cli/roles.rb +215 -55
  41. data/lib/morpheus/cli/subnets_command.rb +11 -2
  42. data/lib/morpheus/cli/user_settings_command.rb +268 -57
  43. data/lib/morpheus/cli/vdi_allocations_command.rb +159 -0
  44. data/lib/morpheus/cli/vdi_apps_command.rb +317 -0
  45. data/lib/morpheus/cli/vdi_command.rb +359 -0
  46. data/lib/morpheus/cli/vdi_gateways_command.rb +290 -0
  47. data/lib/morpheus/cli/vdi_pools_command.rb +571 -0
  48. data/lib/morpheus/cli/version.rb +1 -1
  49. data/lib/morpheus/rest_client.rb +30 -0
  50. data/lib/morpheus/terminal.rb +15 -7
  51. 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