morpheus-cli 4.1.8 → 4.1.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/lib/morpheus/api/api_client.rb +24 -0
  4. data/lib/morpheus/api/{old_cypher_interface.rb → budgets_interface.rb} +10 -11
  5. data/lib/morpheus/api/cloud_datastores_interface.rb +7 -0
  6. data/lib/morpheus/api/cloud_resource_pools_interface.rb +2 -2
  7. data/lib/morpheus/api/cypher_interface.rb +18 -12
  8. data/lib/morpheus/api/health_interface.rb +72 -0
  9. data/lib/morpheus/api/instances_interface.rb +1 -1
  10. data/lib/morpheus/api/library_instance_types_interface.rb +7 -0
  11. data/lib/morpheus/api/log_settings_interface.rb +6 -0
  12. data/lib/morpheus/api/network_security_servers_interface.rb +30 -0
  13. data/lib/morpheus/api/price_sets_interface.rb +42 -0
  14. data/lib/morpheus/api/prices_interface.rb +68 -0
  15. data/lib/morpheus/api/provisioning_settings_interface.rb +29 -0
  16. data/lib/morpheus/api/servers_interface.rb +1 -1
  17. data/lib/morpheus/api/service_plans_interface.rb +34 -11
  18. data/lib/morpheus/api/task_sets_interface.rb +8 -0
  19. data/lib/morpheus/api/tasks_interface.rb +8 -0
  20. data/lib/morpheus/cli.rb +6 -3
  21. data/lib/morpheus/cli/appliance_settings_command.rb +13 -5
  22. data/lib/morpheus/cli/approvals_command.rb +1 -1
  23. data/lib/morpheus/cli/apps.rb +88 -28
  24. data/lib/morpheus/cli/backup_settings_command.rb +1 -1
  25. data/lib/morpheus/cli/blueprints_command.rb +2 -0
  26. data/lib/morpheus/cli/budgets_command.rb +672 -0
  27. data/lib/morpheus/cli/cli_command.rb +13 -2
  28. data/lib/morpheus/cli/cli_registry.rb +1 -0
  29. data/lib/morpheus/cli/clusters.rb +40 -274
  30. data/lib/morpheus/cli/commands/standard/benchmark_command.rb +114 -66
  31. data/lib/morpheus/cli/commands/standard/coloring_command.rb +12 -0
  32. data/lib/morpheus/cli/commands/standard/curl_command.rb +31 -6
  33. data/lib/morpheus/cli/commands/standard/echo_command.rb +8 -3
  34. data/lib/morpheus/cli/commands/standard/set_prompt_command.rb +1 -1
  35. data/lib/morpheus/cli/containers_command.rb +37 -24
  36. data/lib/morpheus/cli/cypher_command.rb +191 -150
  37. data/lib/morpheus/cli/health_command.rb +903 -0
  38. data/lib/morpheus/cli/hosts.rb +43 -32
  39. data/lib/morpheus/cli/instances.rb +119 -68
  40. data/lib/morpheus/cli/jobs_command.rb +1 -1
  41. data/lib/morpheus/cli/library_instance_types_command.rb +61 -11
  42. data/lib/morpheus/cli/library_option_types_command.rb +2 -2
  43. data/lib/morpheus/cli/log_settings_command.rb +46 -3
  44. data/lib/morpheus/cli/logs_command.rb +24 -17
  45. data/lib/morpheus/cli/mixins/accounts_helper.rb +2 -0
  46. data/lib/morpheus/cli/mixins/logs_helper.rb +73 -19
  47. data/lib/morpheus/cli/mixins/print_helper.rb +29 -1
  48. data/lib/morpheus/cli/mixins/provisioning_helper.rb +554 -96
  49. data/lib/morpheus/cli/mixins/whoami_helper.rb +13 -1
  50. data/lib/morpheus/cli/networks_command.rb +3 -0
  51. data/lib/morpheus/cli/option_types.rb +83 -53
  52. data/lib/morpheus/cli/price_sets_command.rb +543 -0
  53. data/lib/morpheus/cli/prices_command.rb +669 -0
  54. data/lib/morpheus/cli/processes_command.rb +0 -2
  55. data/lib/morpheus/cli/provisioning_settings_command.rb +237 -0
  56. data/lib/morpheus/cli/remote.rb +9 -4
  57. data/lib/morpheus/cli/reports_command.rb +10 -4
  58. data/lib/morpheus/cli/roles.rb +93 -38
  59. data/lib/morpheus/cli/security_groups.rb +10 -0
  60. data/lib/morpheus/cli/service_plans_command.rb +736 -0
  61. data/lib/morpheus/cli/tasks.rb +220 -8
  62. data/lib/morpheus/cli/tenants_command.rb +3 -16
  63. data/lib/morpheus/cli/users.rb +2 -25
  64. data/lib/morpheus/cli/version.rb +1 -1
  65. data/lib/morpheus/cli/whitelabel_settings_command.rb +18 -18
  66. data/lib/morpheus/cli/whoami.rb +28 -10
  67. data/lib/morpheus/cli/workflows.rb +488 -36
  68. data/lib/morpheus/formatters.rb +22 -0
  69. data/morpheus-cli.gemspec +1 -0
  70. metadata +28 -5
  71. data/lib/morpheus/cli/accounts.rb +0 -335
  72. data/lib/morpheus/cli/old_cypher_command.rb +0 -412
@@ -23,6 +23,7 @@ class Morpheus::Cli::SecurityGroups
23
23
  @clouds_interface = @api_client.clouds
24
24
  @options_interface = @api_client.options
25
25
  @active_security_group = ::Morpheus::Cli::SecurityGroups.load_security_group_file
26
+ @network_security_servers = @api_client.network_security_servers
26
27
  end
27
28
 
28
29
  def handle(args)
@@ -353,6 +354,15 @@ class Morpheus::Cli::SecurityGroups
353
354
  #v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'zoneId', 'fieldLabel' => 'Scoped Cloud', 'type' => 'select', 'optionSource' => 'cloudsForSecurityGroup', 'required' => false}], options[:options], @api_client)
354
355
  if !v_prompt['zoneId'].to_s.empty? && v_prompt['zoneId'].to_s != 'all' && v_prompt['zoneId'].to_s != '-1'
355
356
  payload['securityGroup']['zoneId'] = v_prompt['zoneId']
357
+
358
+ zone = find_cloud_by_id(payload['securityGroup']['zoneId'])
359
+ if zone['securityServer']
360
+ sec_server = @network_security_servers.get(zone['securityServer']['id'])['networkSecurityServer']
361
+
362
+ if sec_server['type']
363
+ payload['securityGroup'].deep_merge!(Morpheus::Cli::OptionTypes.prompt(sec_server['type']['optionTypes'], options[:options], @api_client, {zoneId: zone['id']}))
364
+ end
365
+ end
356
366
  end
357
367
  rescue => ex
358
368
  print yellow,"Failed to determine the available scoped clouds.",reset,"\n"
@@ -0,0 +1,736 @@
1
+ require 'morpheus/cli/cli_command'
2
+
3
+ class Morpheus::Cli::ServicePlanCommand
4
+ include Morpheus::Cli::CliCommand
5
+ include Morpheus::Cli::AccountsHelper
6
+ include Morpheus::Cli::ProvisioningHelper
7
+
8
+ set_command_name :'service-plans'
9
+
10
+ register_subcommands :list, :get, :add, :update, :deactivate
11
+ set_default_subcommand :list
12
+
13
+ def connect(opts)
14
+ @api_client = establish_remote_appliance_connection(opts)
15
+ @service_plans_interface = @api_client.service_plans
16
+ @provision_types_interface = @api_client.provision_types
17
+ @options_interface = @api_client.options
18
+ @accounts_interface = @api_client.accounts
19
+ @price_sets_interface = @api_client.price_sets
20
+ end
21
+
22
+ def handle(args)
23
+ handle_subcommand(args)
24
+ end
25
+
26
+ def list(args)
27
+ options = {}
28
+ params = {'includeZones': true}
29
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
30
+ opts.banner = subcommand_usage()
31
+ opts.on('-t', '--provision-type VALUE', String, "Filter by provision type ID or code") do |val|
32
+ options[:provisionType] = val
33
+ end
34
+ opts.on('-i', '--include-inactive [on|off]', String, "Can be used to enable / disable inactive filter. Default is on") do |val|
35
+ params['includeInactive'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
36
+ end
37
+ build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
38
+ opts.footer = "List service plans."
39
+ end
40
+ optparse.parse!(args)
41
+ connect(options)
42
+ if args.count != 0
43
+ raise_command_error "wrong number of arguments, expected 0 and got (#{args.count}) #{args}\n#{optparse}"
44
+ return 1
45
+ end
46
+
47
+ begin
48
+ params.merge!(parse_list_options(options))
49
+
50
+ if !options[:provisionType].nil?
51
+ type = find_provision_type(options[:provisionType])
52
+
53
+ if type.nil?
54
+ print_red_alert "Provision type #{options[:provisionType]} not found"
55
+ exit 1
56
+ end
57
+ params['provisionTypeId'] = type['id']
58
+ end
59
+
60
+ @service_plans_interface.setopts(options)
61
+ if options[:dry_run]
62
+ print_dry_run @service_plans_interface.dry.list(params)
63
+ return
64
+ end
65
+ json_response = @service_plans_interface.list(params)
66
+
67
+ render_result = render_with_format(json_response, options, 'servicePlans')
68
+ return 0 if render_result
69
+
70
+ title = "Morpheus Service Plans"
71
+ subtitles = []
72
+ subtitles += parse_list_subtitles(options)
73
+ print_h1 title, subtitles
74
+
75
+ plans = json_response['servicePlans']
76
+ if plans.empty?
77
+ print yellow,"No service plans found.",reset,"\n"
78
+ else
79
+ rows = plans.collect do |it|
80
+ {
81
+ id: (it['active'] ? cyan : yellow) + it['id'].to_s,
82
+ name: it['name'],
83
+ type: it['provisionType'] ? it['provisionType']['name'] : '',
84
+ active: format_boolean(it['active']),
85
+ cores: it['maxCores'],
86
+ memory: format_bytes(it['maxMemory']),
87
+ clouds: it['zones'] ? truncate_string(it['zones'].collect {|it| it['name']}.join(', '), 30) : '',
88
+ visibility: (it['visibility'] || '').capitalize,
89
+ tenants: it['tenants'] || 'Global',
90
+ price_sets: (it['priceSets'] ? it['priceSets'].count : 0).to_s + cyan
91
+ }
92
+ end
93
+ columns = [
94
+ :id, :name, :type, :active, :cores, :memory, :clouds, :visibility, :tenants, :price_sets
95
+ ]
96
+ columns.delete(:active) if !params['includeInactive']
97
+
98
+ print as_pretty_table(rows, columns, options)
99
+ print_results_pagination(json_response)
100
+ print reset,"\n"
101
+ end
102
+ print reset,"\n"
103
+ return 0
104
+ rescue RestClient::Exception => e
105
+ print_rest_exception(e, options)
106
+ exit 1
107
+ end
108
+ end
109
+
110
+ def get(args)
111
+ options = {}
112
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
113
+ opts.banner = subcommand_usage("[plan]")
114
+ build_common_options(opts, options, [:json, :dry_run, :remote])
115
+ opts.footer = "Get details about a service plan.\n" +
116
+ "[plan] is required. Service plan ID, name or code"
117
+ end
118
+ optparse.parse!(args)
119
+ if args.count != 1
120
+ raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args}\n#{optparse}"
121
+ end
122
+ connect(options)
123
+ return _get(args[0], options)
124
+ end
125
+
126
+ def _get(plan_id, options = {})
127
+ params = {}
128
+ begin
129
+ @service_plans_interface.setopts(options)
130
+
131
+ if !(plan_id.to_s =~ /\A\d{1,}\Z/)
132
+ plan = find_service_plan(plan_id)
133
+
134
+ if !plan
135
+ print_red_alert "Service plan #{plan_id} not found"
136
+ exit 1
137
+ end
138
+ plan_id = plan['id']
139
+ end
140
+
141
+ if options[:dry_run]
142
+ print_dry_run @service_plans_interface.dry.get(plan_id)
143
+ return
144
+ end
145
+ json_response = @service_plans_interface.get(plan_id)
146
+
147
+ render_result = render_with_format(json_response, options, 'servicePlan')
148
+ return 0 if render_result
149
+
150
+ title = "Morpheus Service Plan"
151
+ subtitles = []
152
+ subtitles += parse_list_subtitles(options)
153
+ print_h1 title, subtitles
154
+
155
+ service_plan = json_response['servicePlan']
156
+ print cyan
157
+ description_cols = {
158
+ "ID" => lambda {|it| it['id']},
159
+ "Name" => lambda {|it| it['name'] || (it['accountIntegration'] ? 'Pending' : 'Not Set')},
160
+ "Active" => lambda {|it| format_boolean(it['active'])},
161
+ "Visibility" => lambda {|it| (it['visibility'] || '').capitalize},
162
+ "Code" => lambda {|it| it['code']},
163
+ "Display Order" => lambda {|it| it['sortOrder']},
164
+ "Provision Type" => lambda {|it| it['provisionType'] ? it['provisionType']['name'] : ''},
165
+ "Storage" => lambda {|it| printable_byte_size(it, it['maxStorage'], 'storageSizeType', 'GB')}
166
+ }
167
+
168
+ provision_type = service_plan['provisionType'] || {}
169
+
170
+ description_cols['Customize Root Volume'] = lambda {|it| format_boolean(it['customMaxStorage'])} if provision_type['rootDiskCustomizable']
171
+ description_cols['Customize Extra Volumes'] = lambda {|it| format_boolean(it['customMaxDataStorage'])} if provision_type['customizeVolume']
172
+ description_cols['Add Volumes'] = lambda {|it| format_boolean(it['addVolumes'])} if provision_type['addVolumes']
173
+ description_cols['Max Disks Allowed'] = lambda {|it| it['maxDisks'] || 0} if provision_type['addVolumes']
174
+ description_cols['Memory'] = lambda {|it| printable_byte_size(it, it['maxMemory'], 'memorySizeType')}
175
+ description_cols['Custom Max Memory'] = lambda {|it| format_boolean(it['customMaxMemory'])}
176
+ description_cols['Core Count'] = lambda {|it| it['maxCores']}
177
+ description_cols['Custom Cores'] = lambda {|it| format_boolean(it['customCores'])}
178
+ description_cols['Cores Per Socket'] = lambda {|it| it['coresPerSocket']} if provision_type['hasConfigurableCpuSockets'] && service_plan['customCores']
179
+
180
+ ranges = (service_plan['config'] ? service_plan['config']['ranges'] : nil) || {}
181
+
182
+ if (ranges['minStorage'] && ranges['minStorage'] != '') || (ranges['maxStorage'] && ranges['maxStorage'] != '')
183
+ description_cols['Custom Storage Range'] = lambda {|it|
184
+ get_range(
185
+ ranges['minStorage'] && ranges['minStorage'] != '' ? "#{ranges['minStorage']} #{(it['config'] && it['config']['storageSizeType'] ? it['config']['storageSizeType'] : 'GB').upcase}" : nil,
186
+ ranges['maxStorage'] && ranges['maxStorage'] != '' ? "#{ranges['maxStorage']} #{(it['config'] && it['config']['storageSizeType'] ? it['config']['storageSizeType'] : 'GB').upcase}" : nil,
187
+ )
188
+ }
189
+ end
190
+ if (ranges['minMemory'] && ranges['minMemory'] != '') || (ranges['maxMemory'] && ranges['maxMemory'] != '')
191
+ description_cols['Custom Memory Range'] = lambda {|it|
192
+ get_range(
193
+ ranges['minMemory'] && ranges['minMemory'] != '' ? printable_byte_size(it, ranges['minMemory'], 'memorySizeType') : nil,
194
+ ranges['maxMemory'] && ranges['maxMemory'] != '' ? printable_byte_size(it, ranges['maxMemory'], 'memorySizeType') : nil
195
+ )
196
+ }
197
+ end
198
+ if (ranges['minCores'] && ranges['minCores'] != '') || (ranges['maxCores'] && ranges['maxCores'] != '')
199
+ description_cols['Custom Cores Range'] = lambda {|it|
200
+ get_range(
201
+ ranges['minCores'] && ranges['minCores'] != '' ? ranges['minCores'] : nil,
202
+ ranges['maxCores'] && ranges['maxCores'] != '' ? ranges['maxCores'] : nil
203
+ )
204
+ }
205
+ end
206
+
207
+ print_description_list(description_cols, service_plan)
208
+
209
+ print_h2 "Price Sets"
210
+ price_sets = service_plan['priceSets']
211
+
212
+ if price_sets && !price_sets.empty?
213
+ rows = price_sets.collect do |it|
214
+ {
215
+ id: it['id'],
216
+ unit: (it['priceUnit'] || '').capitalize,
217
+ name: it['name']
218
+ }
219
+ end
220
+ columns = [
221
+ :id, :unit, :name
222
+ ]
223
+ print as_pretty_table(rows, columns, options)
224
+ else
225
+ print yellow,"No price sets.",reset,"\n"
226
+ end
227
+
228
+ print_permissions(service_plan['permissions'], ['plans', 'groupDefaults'])
229
+ print reset,"\n"
230
+ return 0
231
+ rescue RestClient::Exception => e
232
+ print_rest_exception(e, options)
233
+ exit 1
234
+ end
235
+ end
236
+
237
+ def add(args)
238
+ options = {}
239
+ params = {}
240
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
241
+ opts.banner = subcommand_usage()
242
+ opts.on("--name NAME", String, "Service plan name") do |val|
243
+ params['name'] = val.to_s
244
+ end
245
+ opts.on("--code CODE", String, "Service plan code, unique identifier") do |val|
246
+ params['code'] = val.to_s
247
+ end
248
+ opts.on('-t', '--provision-type [TYPE]', String, "Provision type ID or code") do |val|
249
+ options[:provisionType] = val
250
+ end
251
+ opts.on("--description [TEXT]", String, "Description") do |val|
252
+ params['description'] = val.to_s
253
+ end
254
+ opts.on('--editable [on|off]', String, "Can be used to enable / disable the editability of the service plan. Default is on") do |val|
255
+ params['editable'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
256
+ end
257
+ opts.on('--storage [AMOUNT]', String, "Storage size is required. Assumes GB unless optional modifier specified, ex: 512MB" ) do |val|
258
+ bytes = parse_bytes_param(val, '--storage', 'GB')
259
+ params['maxStorage'] = bytes[:bytes]
260
+ (params['config'] ||= {})['storageSizeType'] = bytes[:unit].downcase
261
+ end
262
+ opts.on('--memory [AMOUNT]', String, "Memory size is required. Assumes MB unless optional modifier specified, ex: 1GB" ) do |val|
263
+ bytes = parse_bytes_param(val, '--memory', 'MB')
264
+ params['maxMemory'] = bytes[:bytes]
265
+ (params['config'] ||= {})['memorySizeType'] = bytes[:unit].downcase
266
+ end
267
+ opts.on('--cores [NUMBER]', Integer, "Core count. Default is 1" ) do |val|
268
+ params['maxCores'] = val.to_i || 1
269
+ end
270
+ opts.on('--disks [NUMBER]', Integer, "Max disks allowed" ) do |val|
271
+ params['maxDisks'] = val.to_i || 1
272
+ end
273
+ opts.on('--custom-cores [on|off]', String, "Can be used to enable / disable customizable cores. Default is on") do |val|
274
+ params['customCores'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
275
+ end
276
+ opts.on('--custom-storage [on|off]', String, "Can be used to enable / disable customizable storage. Default is on") do |val|
277
+ params['customMaxStorage'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
278
+ end
279
+ opts.on('--custom-volumes [on|off]', String, "Can be used to enable / disable customizable extra volumes. Default is on") do |val|
280
+ params['customMaxDataStorage'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
281
+ end
282
+ opts.on('--custom-memory [on|off]', String, "Can be used to enable / disable customizable memory. Default is on") do |val|
283
+ params['customMaxMemory'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
284
+ end
285
+ opts.on('--add-volumes [on|off]', String, "Can be used to enable / disable ability to add volumes. Default is on") do |val|
286
+ params['addVolumes'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
287
+ end
288
+ opts.on('--sort-order NUMBER', Integer, "Sort order") do |val|
289
+ params['sortOrder'] = val.to_i
290
+ end
291
+ opts.on('--price-sets [LIST]', Array, 'Price set(s), comma separated list of price set IDs') do |list|
292
+ params['priceSets'] = list.collect {|it| it.to_s.strip.empty? || !it.to_i ? nil : it.to_s.strip}.compact.uniq.collect {|it| {'id' => it.to_i}}
293
+ end
294
+ opts.on('--min-storage NUMBER', String, "Min storage. Assumes GB unless optional modifier specified, ex: 512MB") do |val|
295
+ # Storage doesn't get converted to bytes
296
+ bytes = parse_bytes_param(val, '--min-storage', 'GB')
297
+ ((params['config'] ||= {})['ranges'] ||= {})['minStorage'] = bytes[:number]
298
+ (params['config'] ||= {})['storageSizeType'] = bytes[:unit].downcase
299
+ end
300
+ opts.on('--max-storage NUMBER', String, "Max storage. Assumes GB unless optional modifier specified, ex: 512MB") do |val|
301
+ # Storage doesn't get converted to bytes
302
+ bytes = parse_bytes_param(val, '--max-storage', 'GB')
303
+ ((params['config'] ||= {})['ranges'] ||= {})['maxStorage'] = bytes[:number]
304
+ (params['config'] ||= {})['storageSizeType'] = bytes[:unit].downcase
305
+ end
306
+ opts.on('--min-memory NUMBER', String, "Min memory. Assumes MB unless optional modifier specified, ex: 1GB") do |val|
307
+ # Memory does get converted to bytes
308
+ bytes = parse_bytes_param(val, '--min-memory', 'MB')
309
+ ((params['config'] ||= {})['ranges'] ||= {})['minMemory'] = bytes[:bytes]
310
+ (params['config'] ||= {})['memorySizeType'] = bytes[:unit].downcase
311
+ end
312
+ opts.on('--max-memory NUMBER', String, "Max memory. Assumes MB unless optional modifier specified, ex: 1GB") do |val|
313
+ # Memory does get converted to bytes
314
+ bytes = parse_bytes_param(val, '--max-memory', 'MB')
315
+ ((params['config'] ||= {})['ranges'] ||= {})['maxMemory'] = bytes[:bytes]
316
+ (params['config'] ||= {})['memorySizeType'] = bytes[:unit].downcase
317
+ end
318
+ opts.on('--min-cores NUMBER', String, "Min cores") do |val|
319
+ ((params['config'] ||= {})['ranges'] ||= {})['minCores'] = val.to_i
320
+ end
321
+ opts.on('--max-cores NUMBER', String, "Max cores") do |val|
322
+ ((params['config'] ||= {})['ranges'] ||= {})['maxCores'] = val.to_i
323
+ end
324
+ add_perms_options(opts, options, ['plans', 'groupDefaults'])
325
+ build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote, :quiet])
326
+ opts.footer = "Create service plan"
327
+ end
328
+ optparse.parse!(args)
329
+ connect(options)
330
+ if args.count != 0
331
+ raise_command_error "wrong number of arguments, expected 0 and got (#{args.count}) #{args}\n#{optparse}"
332
+ return 1
333
+ end
334
+
335
+ begin
336
+ payload = parse_payload(options)
337
+
338
+ if !payload
339
+ # name
340
+ params['name'] ||= Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'name', 'type' => 'text', 'fieldLabel' => 'Service Plan Name', 'required' => true, 'description' => 'Service Plan Name.'}],options[:options],@api_client,{}, options[:no_prompt])['name']
341
+
342
+ # code
343
+ params['code'] ||= Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'code', 'type' => 'text', 'fieldLabel' => 'Service Plan Code', 'required' => true, 'defaultValue' => params['name'].gsub(/[^0-9a-z ]/i, '').gsub(' ', '.').downcase, 'description' => 'Service Plan Code.'}],options[:options],@api_client,{}, options[:no_prompt])['code']
344
+
345
+ # provision type
346
+ options[:provisionType] = options[:provisionType] || (args.count > 1 ? args[1] : nil)
347
+ provision_types = @service_plans_interface.provision_types()['provisionTypes']
348
+
349
+ if options[:provisionType].nil? && !options[:no_prompt]
350
+ provision_type_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'provisionType', 'type' => 'select', 'fieldLabel' => 'Provision Type', 'selectOptions' => provision_types.collect {|it| {'name' => it['name'], 'value' => it['id']}}, 'required' => true, 'description' => 'Select Provision Type.'}],options[:options],@api_client,{}, options[:no_prompt], true)['provisionType']
351
+
352
+ if !provision_type_id.nil?
353
+ provision_type = provision_types.find {|it| it['id'] == provision_type_id}
354
+ end
355
+ else
356
+ provision_type = provision_types.find {|it| it['name'] == options[:provisionType] || it['code'] == options[:provisionType] || it['id'] == options[:provisionType].to_i}
357
+
358
+ if provision_type.nil?
359
+ print_red_alert "Provision type #{options[:provisionType]} not found"
360
+ exit 1
361
+ end
362
+ end
363
+
364
+ params['provisionType'] = {'id' => provision_type['id']} if !provision_type.nil?
365
+
366
+ # storage is required
367
+ if params['maxStorage'].nil?
368
+ if options[:no_prompt]
369
+ print_red_alert "Storage size is required"
370
+ exit 1
371
+ end
372
+ while params['maxStorage'].nil? do
373
+ begin
374
+ bytes = parse_bytes_param(
375
+ Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'storage', 'type' => 'text', 'fieldLabel' => 'Storage (GB) [can use MB modifier]', 'required' => true, 'description' => 'Storage (GB)'}],options[:options],@api_client,{}, options[:no_prompt])['storage'],
376
+ 'storage',
377
+ 'GB'
378
+ )
379
+ params['maxStorage'] = bytes[:bytes]
380
+ # (params['config'] ||= {})['storageSizeType'] = bytes[:unit].downcase
381
+ rescue
382
+ print "Invalid Value... Please try again.\n"
383
+ end
384
+ end
385
+ end
386
+
387
+ # memory is required
388
+ if params['maxMemory'].nil?
389
+ if options[:no_prompt]
390
+ print_red_alert "Memory size is required"
391
+ exit 1
392
+ end
393
+ while params['maxMemory'].nil? do
394
+ begin
395
+ bytes = parse_bytes_param(
396
+ Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'memory', 'type' => 'text', 'fieldLabel' => 'Memory (MB) [can use GB modifier]', 'required' => true, 'description' => 'Memory (MB)'}],options[:options],@api_client,{}, options[:no_prompt])['memory'],
397
+ 'memory',
398
+ 'MB'
399
+ )
400
+ params['maxMemory'] = bytes[:bytes]
401
+ # (params['config'] ||= {})['memorySizeType'] = bytes[:unit].downcase
402
+ rescue
403
+ print "Invalid Value... Please try again.\n"
404
+ end
405
+ end
406
+ end
407
+
408
+ # price sets
409
+ if params['priceSets'].nil? && !options[:no_prompt]
410
+ price_sets = []
411
+ while Morpheus::Cli::OptionTypes.confirm("Add #{price_sets.empty? ? '' : 'another '}price set?", {:default => false}) do
412
+ price_unit = prompt_price_unit(options)
413
+
414
+ avail_price_sets ||= @price_sets_interface.list['priceSets'].collect {|it| {'name' => it['name'], 'value' => it['id'], 'priceUnit' => it['priceUnit']}}
415
+
416
+ if price_set_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'priceSet', 'type' => 'select', 'fieldLabel' => 'Price Set', 'selectOptions' => avail_price_sets.reject {|it| it['priceUnit'] != price_unit}, 'required' => false, 'description' => 'Select Price.'}],options[:options],@api_client,{}, options[:no_prompt], true)['priceSet']
417
+ price_set = avail_price_sets.find {|it| it['value'] == price_set_id}
418
+ price_sets << {'id' => price_set['value'], 'priceUnit' => price_set['priceUnit']}
419
+ avail_price_sets.reject! {|it| it['value'] == price_set_id}
420
+ end
421
+
422
+ if avail_price_sets.empty?
423
+ break
424
+ end
425
+ end
426
+ params['priceSets'] = price_sets if !price_sets.empty?
427
+ end
428
+
429
+ # permissions
430
+ if !options[:no_prompt]
431
+ perms = prompt_permissions(options, ['plans', 'groupDefaults'])
432
+ params['visibility'] = perms.delete('resourcePool')['visibility']
433
+ params['permissions'] = perms
434
+ end
435
+ payload = {'servicePlan' => params}
436
+ end
437
+
438
+ @service_plans_interface.setopts(options)
439
+ if options[:dry_run]
440
+ print_dry_run @service_plans_interface.dry.create(payload)
441
+ return
442
+ end
443
+ json_response = @service_plans_interface.create(payload)
444
+
445
+ if options[:json]
446
+ puts as_json(json_response, options)
447
+ elsif !options[:quiet]
448
+ if json_response['success']
449
+ print_green_success "Service plan created"
450
+ _get(json_response['id'], options)
451
+ else
452
+ print_red_alert "Error creating service plan: #{json_response['msg'] || json_response['errors']}"
453
+ end
454
+ end
455
+ return 0
456
+ rescue RestClient::Exception => e
457
+ print_rest_exception(e, options)
458
+ exit 1
459
+ end
460
+ end
461
+
462
+ def update(args)
463
+ options = {}
464
+ params = {}
465
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
466
+ opts.banner = subcommand_usage("[plan]")
467
+ opts.on("--name NAME", String, "Service plan name") do |val|
468
+ params['name'] = val.to_s
469
+ end
470
+ opts.on("--code CODE", String, "Service plan code, unique identifier") do |val|
471
+ params['code'] = val.to_s
472
+ end
473
+ opts.on('-t', "--provision-type [TYPE]", String, "Provision type ID or code") do |val|
474
+ options[:provisionType] = val
475
+ end
476
+ opts.on("--description [TEXT]", String, "Description") do |val|
477
+ params['description'] = val.to_s
478
+ end
479
+ opts.on('--editable [on|off]', String, "Can be used to enable / disable the editability of the service plan. Default is on") do |val|
480
+ params['editable'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
481
+ end
482
+ opts.on('--storage [AMOUNT]', String, "Storage size is required. Assumes GB unless optional modifier specified, ex: 512MB" ) do |val|
483
+ bytes = parse_bytes_param(val, '--storage', 'GB')
484
+ params['maxStorage'] = bytes[:bytes]
485
+ (params['config'] ||= {})['storageSizeType'] = bytes[:unit].downcase
486
+ end
487
+ opts.on('--memory [AMOUNT]', String, "Memory size is required. Assumes MB unless optional modifier specified, ex: 1GB" ) do |val|
488
+ bytes = parse_bytes_param(val, '--memory', 'MB')
489
+ params['maxMemory'] = bytes[:bytes]
490
+ (params['config'] ||= {})['memorySizeType'] = bytes[:unit].downcase
491
+ end
492
+ opts.on('--cores [NUMBER]', Integer, "Core count. Default is 1" ) do |val|
493
+ params['maxCores'] = val.to_i || 1
494
+ end
495
+ opts.on('--disks [NUMBER]', Integer, "Max disks allowed" ) do |val|
496
+ params['maxDisks'] = val.to_i || 1
497
+ end
498
+ opts.on('--custom-cores [on|off]', String, "Can be used to enable / disable customizable cores. Default is on") do |val|
499
+ params['customCores'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
500
+ end
501
+ opts.on('--custom-storage [on|off]', String, "Can be used to enable / disable customizable storage. Default is on") do |val|
502
+ params['customMaxStorage'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
503
+ end
504
+ opts.on('--custom-volumes [on|off]', String, "Can be used to enable / disable customizable extra volumes. Default is on") do |val|
505
+ params['customMaxDataStorage'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
506
+ end
507
+ opts.on('--custom-memory [on|off]', String, "Can be used to enable / disable customizable memory. Default is on") do |val|
508
+ params['customMaxMemory'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
509
+ end
510
+ opts.on('--add-volumes [on|off]', String, "Can be used to enable / disable ability to add volumes. Default is on") do |val|
511
+ params['addVolumes'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
512
+ end
513
+ opts.on('--sort-order NUMBER', Integer, "Sort order") do |val|
514
+ params['sortOrder'] = val.to_i
515
+ end
516
+ opts.on('--price-sets [LIST]', Array, 'Price set(s), comma separated list of price set IDs') do |list|
517
+ params['priceSets'] = list.collect {|it| it.to_s.strip.empty? || !it.to_i ? nil : it.to_s.strip}.compact.uniq.collect {|it| {'id' => it.to_i}}
518
+ end
519
+ opts.on('--min-storage NUMBER', String, "Min storage. Assumes GB unless optional modifier specified, ex: 512MB") do |val|
520
+ # Storage doesn't get converted to bytes
521
+ bytes = parse_bytes_param(val, '--min-storage', 'GB')
522
+ ((params['config'] ||= {})['ranges'] ||= {})['minStorage'] = bytes[:number]
523
+ (params['config'] ||= {})['storageSizeType'] = bytes[:unit].downcase
524
+ end
525
+ opts.on('--max-storage NUMBER', String, "Max storage. Assumes GB unless optional modifier specified, ex: 512MB") do |val|
526
+ # Storage doesn't get converted to bytes
527
+ bytes = parse_bytes_param(val, '--max-storage', 'GB')
528
+ ((params['config'] ||= {})['ranges'] ||= {})['maxStorage'] = bytes[:number]
529
+ (params['config'] ||= {})['storageSizeType'] = bytes[:unit].downcase
530
+ end
531
+ opts.on('--min-memory NUMBER', String, "Min memory. Assumes MB unless optional modifier specified, ex: 1GB") do |val|
532
+ # Memory does get converted to bytes
533
+ bytes = parse_bytes_param(val, '--min-memory', 'MB')
534
+ ((params['config'] ||= {})['ranges'] ||= {})['minMemory'] = bytes[:bytes]
535
+ (params['config'] ||= {})['memorySizeType'] = bytes[:unit].downcase
536
+ end
537
+ opts.on('--max-memory NUMBER', String, "Max memory. Assumes MB unless optional modifier specified, ex: 1GB") do |val|
538
+ # Memory does get converted to bytes
539
+ bytes = parse_bytes_param(val, '--max-memory', 'MB')
540
+ ((params['config'] ||= {})['ranges'] ||= {})['maxMemory'] = bytes[:bytes]
541
+ (params['config'] ||= {})['memorySizeType'] = bytes[:unit].downcase
542
+ end
543
+ opts.on('--min-cores NUMBER', String, "Min cores") do |val|
544
+ ((params['config'] ||= {})['ranges'] ||= {})['minCores'] = val.to_i
545
+ end
546
+ opts.on('--max-cores NUMBER', String, "Max cores") do |val|
547
+ ((params['config'] ||= {})['ranges'] ||= {})['maxCores'] = val.to_i
548
+ end
549
+ add_perms_options(opts, options, ['plans', 'groupDefaults'])
550
+ build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote, :quiet])
551
+ opts.footer = "Update service plan.\n[plan] is required. Service plan ID, name or code"
552
+ end
553
+ optparse.parse!(args)
554
+ connect(options)
555
+ if args.count != 1
556
+ raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args}\n#{optparse}"
557
+ return 1
558
+ end
559
+
560
+ print options
561
+ begin
562
+ plan = find_service_plan(args[0])
563
+
564
+ if plan.nil?
565
+ print_red_alert "Service plan #{args[0]} not found"
566
+ exit 1
567
+ end
568
+
569
+ payload = parse_payload(options)
570
+
571
+ if !payload
572
+ # provision type
573
+ options[:provisionType] = options[:provisionType] || (args.count > 1 ? args[1] : nil)
574
+
575
+ if !options[:provisionType].nil?
576
+ provision_types = @service_plans_interface.provision_types()['provisionTypes']
577
+ provision_type = provision_types.find {|it| it['name'] == options[:provisionType] || it['code'] == options[:provisionType] || it['id'] == options[:provisionType].to_i}
578
+
579
+ if provision_type.nil?
580
+ print_red_alert "Provision type #{options[:provisionType]} not found"
581
+ exit 1
582
+ end
583
+ params['provisionType'] = {'id' => provision_type['id']} if !provision_type.nil?
584
+ end
585
+
586
+ # perms
587
+ resource_perms = {}
588
+ resource_perms['all'] = true if options[:groupAccessAll]
589
+ resource_perms['sites'] = options[:groupAccessList].collect {|site_id| {'id' => site_id.to_i}} if !options[:groupAccessList].nil?
590
+
591
+ if !resource_perms.empty? || !options[:tenants].nil?
592
+ params['permissions'] = {}
593
+ params['permissions']['resourcePermissions'] = resource_perms if !resource_perms.empty?
594
+ params['permissions']['tenantPermissions'] = {'accounts' => options[:tenants]} if !options[:tenants].nil?
595
+ end
596
+
597
+ # visibility
598
+ params['visibility'] = options[:visibility] if !options[:visibility].nil?
599
+
600
+ payload = {'servicePlan' => params}
601
+ end
602
+
603
+ if payload.empty? || !payload['servicePlan'] || payload['servicePlan'].empty?
604
+ print_green_success "Nothing to update"
605
+ exit 1
606
+ end
607
+
608
+ @service_plans_interface.setopts(options)
609
+ if options[:dry_run]
610
+ print_dry_run @service_plans_interface.dry.update(plan['id'], payload)
611
+ return
612
+ end
613
+ json_response = @service_plans_interface.update(plan['id'], payload)
614
+
615
+ if options[:json]
616
+ puts as_json(json_response, options)
617
+ elsif !options[:quiet]
618
+ if json_response['success']
619
+ print_green_success "Service plan updated"
620
+ _get(plan['id'], options)
621
+ else
622
+ print_red_alert "Error updating service plan: #{json_response['msg'] || json_response['errors']}"
623
+ end
624
+ end
625
+ return 0
626
+ rescue RestClient::Exception => e
627
+ print_rest_exception(e, options)
628
+ exit 1
629
+ end
630
+ end
631
+
632
+ def deactivate(args)
633
+ options = {}
634
+ params = {}
635
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
636
+ opts.banner = subcommand_usage( "[plan]")
637
+ build_common_options(opts, options, [:json, :dry_run, :remote])
638
+ opts.footer = "Deactivate service plan.\n" +
639
+ "[plan] is required. Service plan ID, name or code"
640
+ end
641
+ optparse.parse!(args)
642
+ connect(options)
643
+ if args.count != 1
644
+ raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args}\n#{optparse}"
645
+ return 1
646
+ end
647
+
648
+ begin
649
+ plan = find_service_plan(args[0])
650
+
651
+ if !plan
652
+ print_red_alert "Service plan #{args[0]} not found"
653
+ exit 1
654
+ end
655
+
656
+ if plan['active'] == false
657
+ print_green_success "Service plan #{plan['name']} already deactived."
658
+ return 0
659
+ end
660
+
661
+ unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to deactivate the service plan '#{plan['name']}'?", options)
662
+ return 9, "aborted command"
663
+ end
664
+
665
+ @service_plans_interface.setopts(options)
666
+ if options[:dry_run]
667
+ print_dry_run @service_plans_interface.dry.deactivate(plan['id'], params)
668
+ return
669
+ end
670
+
671
+ json_response = @service_plans_interface.deactivate(plan['id'], params)
672
+
673
+ if options[:json]
674
+ print JSON.pretty_generate(json_response)
675
+ print "\n"
676
+ elsif !options[:quiet]
677
+ print_green_success "Service plan #{plan['name']} deactivated"
678
+ end
679
+ return 0
680
+ rescue RestClient::Exception => e
681
+ print_rest_exception(e, options)
682
+ exit 1
683
+ end
684
+ end
685
+
686
+ private
687
+
688
+ def find_service_plan(val)
689
+ (val.to_s =~ /\A\d{1,}\Z/) ? @service_plans_interface.get(val.to_i)['servicePlan'] : @service_plans_interface.list({'code' => val, 'name' => val})["servicePlans"].first
690
+ end
691
+
692
+ def find_provision_type(val)
693
+ (val.to_s =~ /\A\d{1,}\Z/) ? @provision_types_interface.get(val.to_i)['provisionType'] : @provision_types_interface.list({'name' => val})["provisionTypes"].first
694
+ end
695
+
696
+ def printable_byte_size(plan, val, config_field, default_unit = 'MB')
697
+ label = (((plan['config'] && plan['config'][config_field]) || default_unit) == 'MB' || val.to_i < 1024 * 1024 * 1024) ? 'MB' : 'GB'
698
+ val = (val.to_i || 0) / (label == 'MB' ? 1024 * 1024 : 1024 * 1024 * 1024)
699
+ "#{val} #{label}"
700
+ end
701
+
702
+ def get_range(min_val, max_val)
703
+ if min_val && max_val
704
+ "#{min_val} - #{max_val}"
705
+ elsif min_val
706
+ "> #{min_val}"
707
+ elsif max_val
708
+ "< #{max_val}"
709
+ else
710
+ ""
711
+ end
712
+ end
713
+
714
+ def parse_bytes_param(bytes_param, option, assumed_unit = nil)
715
+ if bytes_param && bytes_param.to_f > 0
716
+ bytes_param.upcase!
717
+ multiplier = 1
718
+ unit = nil
719
+ number = (bytes_param.to_f == bytes_param.to_i ? bytes_param.to_i : bytes_param.to_f)
720
+ if (bytes_param.end_with? 'GB') || ((!bytes_param.end_with? 'MB') && assumed_unit == 'GB')
721
+ unit = 'GB'
722
+ multiplier = 1024 * 1024 * 1024
723
+ elsif (bytes_param.end_with? 'MB') || assumed_unit == 'MB'
724
+ unit = 'MB'
725
+ multiplier = 1024 * 1024
726
+ end
727
+ return {:bytes_param => bytes_param, :bytes => number * multiplier, :number => number, :multiplier => multiplier, :unit => unit}
728
+ end
729
+ raise_command_error "Invalid value for #{option} option"
730
+ end
731
+
732
+ def prompt_price_unit(options)
733
+ price_units = ['Minute', 'Hour', 'Day', 'Month', 'Year', '2 Year', '3 Year', '4 Year', '5 Year'].collect {|it| {'name' => it, 'value' => it.downcase.delete(' ')}}
734
+ Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'priceUnit', 'type' => 'select', 'fieldLabel' => 'Price Unit', 'selectOptions' => price_units, 'required' => true, 'defaultValue' => 'hour', 'description' => 'Select Price Unit.'}],options[:options],@api_client,{})['priceUnit']
735
+ end
736
+ end