morpheus-cli 5.0.2 → 5.2.4

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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/Dockerfile +1 -1
  4. data/lib/morpheus/api/api_client.rb +4 -0
  5. data/lib/morpheus/api/instances_interface.rb +30 -2
  6. data/lib/morpheus/api/invoices_interface.rb +12 -3
  7. data/lib/morpheus/api/servers_interface.rb +7 -0
  8. data/lib/morpheus/api/service_catalog_interface.rb +89 -0
  9. data/lib/morpheus/cli.rb +2 -1
  10. data/lib/morpheus/cli/apps.rb +3 -23
  11. data/lib/morpheus/cli/budgets_command.rb +389 -319
  12. data/lib/morpheus/cli/{catalog_command.rb → catalog_item_types_command.rb} +182 -67
  13. data/lib/morpheus/cli/cli_command.rb +25 -1
  14. data/lib/morpheus/cli/commands/standard/curl_command.rb +25 -10
  15. data/lib/morpheus/cli/commands/standard/history_command.rb +6 -2
  16. data/lib/morpheus/cli/containers_command.rb +0 -24
  17. data/lib/morpheus/cli/cypher_command.rb +6 -2
  18. data/lib/morpheus/cli/dashboard_command.rb +260 -20
  19. data/lib/morpheus/cli/health_command.rb +57 -0
  20. data/lib/morpheus/cli/hosts.rb +128 -11
  21. data/lib/morpheus/cli/instances.rb +270 -108
  22. data/lib/morpheus/cli/invoices_command.rb +67 -4
  23. data/lib/morpheus/cli/jobs_command.rb +94 -92
  24. data/lib/morpheus/cli/library_option_lists_command.rb +1 -1
  25. data/lib/morpheus/cli/library_option_types_command.rb +10 -5
  26. data/lib/morpheus/cli/mixins/accounts_helper.rb +5 -1
  27. data/lib/morpheus/cli/mixins/print_helper.rb +13 -6
  28. data/lib/morpheus/cli/mixins/provisioning_helper.rb +88 -5
  29. data/lib/morpheus/cli/option_types.rb +10 -10
  30. data/lib/morpheus/cli/projects_command.rb +1 -1
  31. data/lib/morpheus/cli/roles.rb +193 -155
  32. data/lib/morpheus/cli/service_catalog_command.rb +1474 -0
  33. data/lib/morpheus/cli/tasks.rb +9 -11
  34. data/lib/morpheus/cli/version.rb +1 -1
  35. data/lib/morpheus/cli/virtual_images.rb +162 -68
  36. data/lib/morpheus/formatters.rb +55 -20
  37. metadata +5 -4
  38. data/lib/morpheus/cli/mixins/catalog_helper.rb +0 -66
@@ -448,7 +448,8 @@ module Morpheus::Cli::PrintHelper
448
448
 
449
449
  if opts[:bar_color] == :rainbow
450
450
  rainbow_bar = ""
451
- cur_rainbow_color = white
451
+ cur_rainbow_color = reset # default terminal color
452
+ rainbow_bar << cur_rainbow_color
452
453
  bars.each_with_index {|bar, i|
453
454
  reached_percent = (i / max_bars.to_f) * 100
454
455
  new_bar_color = cur_rainbow_color
@@ -458,6 +459,8 @@ module Morpheus::Cli::PrintHelper
458
459
  new_bar_color = yellow
459
460
  elsif reached_percent > 10
460
461
  new_bar_color = cyan
462
+ else
463
+ new_bar_color = reset
461
464
  end
462
465
  if cur_rainbow_color != new_bar_color
463
466
  cur_rainbow_color = new_bar_color
@@ -471,7 +474,7 @@ module Morpheus::Cli::PrintHelper
471
474
  #rainbow_bar << " " * padding
472
475
  end
473
476
  rainbow_bar << reset
474
- bar_display = white + "[" + rainbow_bar + white + "]" + " #{cur_rainbow_color}#{percent_label}#{reset}"
477
+ bar_display = cyan + "[" + rainbow_bar + cyan + "]" + " #{cur_rainbow_color}#{percent_label}#{reset}"
475
478
  out << bar_display
476
479
  elsif opts[:bar_color] == :solid
477
480
  bar_color = cyan
@@ -479,12 +482,16 @@ module Morpheus::Cli::PrintHelper
479
482
  bar_color = red
480
483
  elsif percent > 50
481
484
  bar_color = yellow
485
+ elsif percent > 10
486
+ bar_color = cyan
487
+ else
488
+ bar_color = reset
482
489
  end
483
- bar_display = white + "[" + bar_color + bars.join.ljust(max_bars, ' ') + white + "]" + " #{percent_label}" + reset
490
+ bar_display = cyan + "[" + bar_color + bars.join.ljust(max_bars, ' ') + cyan + "]" + " #{percent_label}" + reset
484
491
  out << bar_display
485
492
  else
486
- bar_color = opts[:bar_color] || cyan
487
- bar_display = white + "[" + bar_color + bars.join.ljust(max_bars, ' ') + white + "]" + " #{percent_label}" + reset
493
+ bar_color = opts[:bar_color] || reset
494
+ bar_display = cyan + "[" + bar_color + bars.join.ljust(max_bars, ' ') + cyan + "]" + " #{percent_label}" + reset
488
495
  out << bar_display
489
496
  end
490
497
  return out
@@ -504,7 +511,7 @@ module Morpheus::Cli::PrintHelper
504
511
  out << cyan + "Max CPU".rjust(label_width, ' ') + ": " + generate_usage_bar(cpu_usage.to_f, 100) + "\n"
505
512
  end
506
513
  if opts[:include].include?(:avg_cpu)
507
- cpu_usage = stats['cpuUsageAvg']
514
+ cpu_usage = stats['cpuUsageAvg'] || stats['cpuUsageAverage']
508
515
  out << cyan + "Avg. CPU".rjust(label_width, ' ') + ": " + generate_usage_bar(cpu_usage.to_f, 100) + "\n"
509
516
  end
510
517
  if opts[:include].include?(:cpu)
@@ -596,14 +596,12 @@ module Morpheus::Cli::ProvisioningHelper
596
596
  v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'environment', 'fieldLabel' => 'Environment', 'type' => 'select', 'required' => false, 'selectOptions' => get_available_environments()}], options[:options])
597
597
  payload['instance']['instanceContext'] = v_prompt['environment'] if !v_prompt['environment'].empty?
598
598
 
599
- # Labels (Provisioning API still refers to these as tags)
600
- # and tags (metadata tags) is called metadata.
601
- # todo: switch this from 'tags' to labels' when the api changes
599
+ # Labels (used to be called tags)
602
600
  if options[:labels]
603
- payload['instance']['tags'] = options[:labels].is_a?(Array) ? options[:labels] : options[:labels].to_s.split(',').collect {|it| it.to_s.strip }.compact.uniq
601
+ payload['instance']['labels'] = options[:labels].is_a?(Array) ? options[:labels] : options[:labels].to_s.split(',').collect {|it| it.to_s.strip }.compact.uniq
604
602
  else
605
603
  v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'labels', 'fieldLabel' => 'Labels', 'type' => 'text', 'required' => false}], options[:options])
606
- payload['instance']['tags'] = v_prompt['tags'].split(',').collect {|it| it.to_s.strip }.compact.uniq if !v_prompt['tags'].empty?
604
+ payload['instance']['labels'] = v_prompt['labels'].split(',').collect {|it| it.to_s.strip }.compact.uniq if !v_prompt['labels'].empty?
607
605
  end
608
606
 
609
607
  # Version and Layout
@@ -1693,6 +1691,15 @@ module Morpheus::Cli::ProvisioningHelper
1693
1691
  row = {}
1694
1692
  row['name'] = metadata_pair[0].to_s.strip
1695
1693
  row['value'] = metadata_pair[1].to_s.strip
1694
+ # hacky way to set masked flag to true of false to (masked) in the value itself
1695
+ if(row['value'].include?("(masked)"))
1696
+ row['value'] = row['value'].gsub("(masked)", "").strip
1697
+ row['masked'] = true
1698
+ end
1699
+ if(row['value'].include?("(unmasked)"))
1700
+ row['value'] = row['value'].gsub("(unmasked)", "").strip
1701
+ row['masked'] = false
1702
+ end
1696
1703
  row
1697
1704
  end
1698
1705
  metadata = metadata_list
@@ -2180,6 +2187,82 @@ module Morpheus::Cli::ProvisioningHelper
2180
2187
  return ports
2181
2188
  end
2182
2189
 
2190
+ def format_instance_status(instance, return_color=cyan)
2191
+ out = ""
2192
+ status_string = instance['status'].to_s
2193
+ if status_string == 'running'
2194
+ out << "#{green}#{status_string.upcase}#{return_color}"
2195
+ elsif status_string == 'provisioning'
2196
+ out << "#{cyan}#{status_string.upcase}#{return_color}"
2197
+ elsif status_string == 'stopped' or status_string == 'failed'
2198
+ out << "#{red}#{status_string.upcase}#{return_color}"
2199
+ else
2200
+ out << "#{yellow}#{status_string.upcase}#{return_color}"
2201
+ end
2202
+ out
2203
+ end
2204
+
2205
+ def format_instance_connection_string(instance)
2206
+ if !instance['connectionInfo'].nil? && instance['connectionInfo'].empty? == false
2207
+ connection_string = "#{instance['connectionInfo'][0]['ip']}:#{instance['connectionInfo'][0]['port']}"
2208
+ end
2209
+ end
2210
+
2211
+ def format_app_status(app, return_color=cyan)
2212
+ out = ""
2213
+ status_string = app['status'] || app['appStatus'] || ''
2214
+ if status_string == 'running'
2215
+ out = "#{green}#{status_string.upcase}#{return_color}"
2216
+ elsif status_string == 'provisioning'
2217
+ out = "#{cyan}#{status_string.upcase}#{cyan}"
2218
+ elsif status_string == 'stopped' or status_string == 'failed'
2219
+ out = "#{red}#{status_string.upcase}#{return_color}"
2220
+ elsif status_string == 'unknown'
2221
+ out = "#{yellow}#{status_string.upcase}#{return_color}"
2222
+ elsif status_string == 'warning' && app['instanceCount'].to_i == 0
2223
+ # show this instead of WARNING
2224
+ out = "#{cyan}EMPTY#{return_color}"
2225
+ else
2226
+ out = "#{yellow}#{status_string.upcase}#{return_color}"
2227
+ end
2228
+ out
2229
+ end
2230
+
2231
+ def format_container_status(container, return_color=cyan)
2232
+ out = ""
2233
+ status_string = container['status'].to_s
2234
+ if status_string == 'running'
2235
+ out << "#{green}#{status_string.upcase}#{return_color}"
2236
+ elsif status_string == 'provisioning'
2237
+ out << "#{cyan}#{status_string.upcase}#{return_color}"
2238
+ elsif status_string == 'stopped' or status_string == 'failed'
2239
+ out << "#{red}#{status_string.upcase}#{return_color}"
2240
+ else
2241
+ out << "#{yellow}#{status_string.upcase}#{return_color}"
2242
+ end
2243
+ out
2244
+ end
2245
+
2246
+ def format_container_connection_string(container)
2247
+ if !container['ports'].nil? && container['ports'].empty? == false
2248
+ connection_string = "#{container['ip']}:#{container['ports'][0]['external']}"
2249
+ else
2250
+ # eh? more logic needed here i think, see taglib morph:containerLocationMenu
2251
+ connection_string = "#{container['ip']}"
2252
+ end
2253
+ end
2254
+
2255
+ def format_instance_container_display_name(instance, plural=false)
2256
+ #<span class="info-label">${[null,'docker'].contains(instance.layout?.provisionType?.code) ? 'Containers' : 'Virtual Machines'}:</span> <span class="info-value">${instance.containers?.size()}</span>
2257
+ v = plural ? "Containers" : "Container"
2258
+ if instance && instance['layout'] && instance['layout'].key?("provisionTypeCode")
2259
+ if [nil, 'docker'].include?(instance['layout']["provisionTypeCode"])
2260
+ v = plural ? "Virtual Machines" : "Virtual Machine"
2261
+ end
2262
+ end
2263
+ return v
2264
+ end
2265
+
2183
2266
  def format_blueprint_type(type_code)
2184
2267
  return type_code.to_s # just show it as is
2185
2268
  if type_code.to_s.empty?
@@ -142,25 +142,25 @@ module Morpheus
142
142
  value = value.to_s.include?('.') ? value.to_f : value.to_i
143
143
  # these select prompts should just fall down through below, with the extra params no_prompt, use_value
144
144
  elsif option_type['type'] == 'select'
145
- value = select_prompt(option_type.merge({'defaultValue' => value, 'defaultInputValue' => input_value}), api_client, (api_params || {}).merge(results), true)
145
+ value = select_prompt(option_type.merge({'defaultValue' => value, 'defaultInputValue' => input_value}), api_client, (option_type['noParams'] ? {} : (api_params || {}).merge(results)), true)
146
146
  elsif option_type['type'] == 'multiSelect'
147
147
  # support value as csv like "thing1, thing2"
148
148
  value_list = value.is_a?(String) ? value.parse_csv.collect {|v| v ? v.to_s.strip : v } : [value].flatten
149
149
  input_value_list = input_value.is_a?(String) ? input_value.parse_csv.collect {|v| v ? v.to_s.strip : v } : [input_value].flatten
150
150
  select_value_list = []
151
151
  value_list.each_with_index do |v, i|
152
- select_value_list << select_prompt(option_type.merge({'defaultValue' => v, 'defaultInputValue' => input_value_list[i]}), api_client, (api_params || {}).merge(results), true)
152
+ select_value_list << select_prompt(option_type.merge({'defaultValue' => v, 'defaultInputValue' => input_value_list[i]}), api_client, (option_type['noParams'] ? {} : (api_params || {}).merge(results)), true)
153
153
  end
154
154
  value = select_value_list
155
155
  elsif option_type['type'] == 'typeahead'
156
- value = typeahead_prompt(option_type.merge({'defaultValue' => value, 'defaultInputValue' => input_value}), api_client, (api_params || {}).merge(results), true)
156
+ value = typeahead_prompt(option_type.merge({'defaultValue' => value, 'defaultInputValue' => input_value}), api_client, (option_type['noParams'] ? {} : (api_params || {}).merge(results)), true)
157
157
  elsif option_type['type'] == 'multiTypeahead'
158
158
  # support value as csv like "thing1, thing2"
159
159
  value_list = value.is_a?(String) ? value.parse_csv.collect {|v| v ? v.to_s.strip : v } : [value].flatten
160
160
  input_value_list = input_value.is_a?(String) ? input_value.parse_csv.collect {|v| v ? v.to_s.strip : v } : [input_value].flatten
161
161
  select_value_list = []
162
162
  value_list.each_with_index do |v, i|
163
- select_value_list << typeahead_prompt(option_type.merge({'defaultValue' => v, 'defaultInputValue' => input_value_list[i]}), api_client, (api_params || {}).merge(results), true)
163
+ select_value_list << typeahead_prompt(option_type.merge({'defaultValue' => v, 'defaultInputValue' => input_value_list[i]}), api_client, (option_type['noParams'] ? {} : (api_params || {}).merge(results)), true)
164
164
  end
165
165
  value = select_value_list
166
166
  end
@@ -187,11 +187,11 @@ module Morpheus
187
187
  # select type is special because it supports skipSingleOption
188
188
  # and prints the available options on error
189
189
  if ['select', 'multiSelect'].include?(option_type['type'])
190
- value = select_prompt(option_type, api_client, (api_params || {}).merge(results), true)
190
+ value = select_prompt(option_type, api_client, (option_type['noParams'] ? {} : (api_params || {}).merge(results)), true)
191
191
  value_found = !!value
192
192
  end
193
193
  if ['typeahead', 'multiTypeahead'].include?(option_type['type'])
194
- value = typeahead_prompt(option_type, api_client, (api_params || {}).merge(results), true)
194
+ value = typeahead_prompt(option_type, api_client, (option_type['noParams'] ? {} : (api_params || {}).merge(results)), true)
195
195
  value_found = !!value
196
196
  end
197
197
  if !value_found
@@ -228,11 +228,11 @@ module Morpheus
228
228
  # I suppose the entered value should take precedence
229
229
  # api_params = api_params.merge(options) # this might be good enough
230
230
  # dup it
231
- value = select_prompt(option_type, api_client, (api_params || {}).merge(results), options[:no_prompt], nil, paging_enabled)
231
+ value = select_prompt(option_type, api_client, (option_type['noParams'] ? {} : (api_params || {}).merge(results)), options[:no_prompt], nil, paging_enabled)
232
232
  if value && option_type['type'] == 'multiSelect'
233
233
  value = [value]
234
234
  while self.confirm("Add another #{option_type['fieldLabel']}?", {:default => false}) do
235
- if addn_value = select_prompt(option_type, api_client, (api_params || {}).merge(results), options[:no_prompt], nil, paging_enabled)
235
+ if addn_value = select_prompt(option_type, api_client, (option_type['noParams'] ? {} : (api_params || {}).merge(results)), options[:no_prompt], nil, paging_enabled)
236
236
  value << addn_value
237
237
  else
238
238
  break
@@ -240,11 +240,11 @@ module Morpheus
240
240
  end
241
241
  end
242
242
  elsif ['typeahead', 'multiTypeahead'].include?(option_type['type'])
243
- value = typeahead_prompt(option_type, api_client, (api_params || {}).merge(results), options[:no_prompt], nil, paging_enabled)
243
+ value = typeahead_prompt(option_type, api_client, (option_type['noParams'] ? {} : (api_params || {}).merge(results)), options[:no_prompt], nil, paging_enabled)
244
244
  if value && option_type['type'] == 'multiTypeahead'
245
245
  value = [value]
246
246
  while self.confirm("Add another #{option_type['fieldLabel']}?", {:default => false}) do
247
- if addn_value = typeahead_prompt(option_type, api_client, (api_params || {}).merge(results), options[:no_prompt], nil, paging_enabled)
247
+ if addn_value = typeahead_prompt(option_type, api_client, (option_type['noParams'] ? {} : (api_params || {}).merge(results)), options[:no_prompt], nil, paging_enabled)
248
248
  value << addn_value
249
249
  else
250
250
  break
@@ -42,7 +42,7 @@ class Morpheus::Cli::Projects
42
42
  opts.on('--owners [LIST]', Array, "Owner, comma separated list of usernames or IDs.") do |list|
43
43
  owner_ids = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
44
44
  end
45
- build_standard_get_options(opts, options)
45
+ build_standard_list_options(opts, options)
46
46
  opts.footer = <<-EOT
47
47
  List projects.
48
48
  EOT
@@ -211,56 +211,70 @@ EOT
211
211
  print cyan,"Use --permissions to list permissions","\n"
212
212
  end
213
213
 
214
+ has_group_access = true
215
+ has_cloud_access = true
214
216
  print_h2 "Global Access", options
215
- puts as_pretty_table([json_response], [
216
- {"Groups" => lambda {|it| get_access_string(it['globalSiteAccess']) } },
217
- {"Clouds" => lambda {|it| get_access_string(it['globalZoneAccess']) } },
218
- {"Instance Types" => lambda {|it| get_access_string(it['globalInstanceTypeAccess']) } },
219
- {"Blueprints" => lambda {|it| get_access_string(it['globalAppTemplateAccess'] || it['globalBlueprintAccess']) } },
220
- {"Catalog Item Types" => lambda {|it| get_access_string(it['globalCatalogItemTypeAccess']) } },
221
- ], options)
222
-
223
- #print_h2 "Group Access: #{get_access_string(json_response['globalSiteAccess'])}", options
224
- print cyan
225
- if json_response['globalSiteAccess'] == 'custom'
226
- print_h2 "Group Access", options
227
- if options[:include_group_access]
228
- rows = json_response['sites'].collect do |it|
229
- {
230
- name: it['name'],
231
- access: format_access_string(it['access'], ["none","read","full"]),
232
- }
217
+ global_access_columns = {
218
+ "Groups" => lambda {|it| get_access_string(it['globalSiteAccess']) },
219
+ "Clouds" => lambda {|it| get_access_string(it['globalZoneAccess']) },
220
+ "Instance Types" => lambda {|it| get_access_string(it['globalInstanceTypeAccess']) },
221
+ "Blueprints" => lambda {|it| get_access_string(it['globalAppTemplateAccess'] || it['globalBlueprintAccess']) },
222
+ "Catalog Item Types" => lambda {|it| get_access_string(it['globalCatalogItemTypeAccess']) },
223
+ }
224
+ if role['roleType'].to_s.downcase == 'account'
225
+ global_access_columns.delete("Groups")
226
+ has_group_access = false
227
+ else
228
+ global_access_columns.delete("Clouds")
229
+ has_cloud_access = false
230
+ end
231
+ puts as_pretty_table([json_response], global_access_columns, options)
232
+
233
+ if has_group_access
234
+ #print_h2 "Group Access: #{get_access_string(json_response['globalSiteAccess'])}", options
235
+ print cyan
236
+ if json_response['globalSiteAccess'] == 'custom'
237
+ print_h2 "Group Access", options
238
+ if options[:include_group_access]
239
+ rows = json_response['sites'].collect do |it|
240
+ {
241
+ name: it['name'],
242
+ access: format_access_string(it['access'], ["none","read","full"]),
243
+ }
244
+ end
245
+ print as_pretty_table(rows, [:name, :access], options)
246
+ else
247
+ print cyan,"Use -g, --group-access to list custom access","\n"
233
248
  end
234
- print as_pretty_table(rows, [:name, :access], options)
249
+ print reset,"\n"
235
250
  else
236
- print cyan,"Use -g, --group-access to list custom access","\n"
251
+ # print "\n"
252
+ # print cyan,bold,"Group Access: #{get_access_string(json_response['globalSiteAccess'])}",reset,"\n"
237
253
  end
238
- print reset,"\n"
239
- else
240
- # print "\n"
241
- # print cyan,bold,"Group Access: #{get_access_string(json_response['globalSiteAccess'])}",reset,"\n"
242
254
  end
243
255
 
244
- print cyan
245
- #puts "Cloud Access: #{get_access_string(json_response['globalZoneAccess'])}"
246
- #print "\n"
247
- if json_response['globalZoneAccess'] == 'custom'
248
- print_h2 "Cloud Access", options
249
- if options[:include_cloud_access]
250
- rows = json_response['zones'].collect do |it|
251
- {
252
- name: it['name'],
253
- access: format_access_string(it['access'], ["none","read","full"]),
254
- }
256
+ if has_cloud_access
257
+ print cyan
258
+ #puts "Cloud Access: #{get_access_string(json_response['globalZoneAccess'])}"
259
+ #print "\n"
260
+ if json_response['globalZoneAccess'] == 'custom'
261
+ print_h2 "Cloud Access", options
262
+ if options[:include_cloud_access]
263
+ rows = json_response['zones'].collect do |it|
264
+ {
265
+ name: it['name'],
266
+ access: format_access_string(it['access'], ["none","read","full"]),
267
+ }
268
+ end
269
+ print as_pretty_table(rows, [:name, :access], options)
270
+ else
271
+ print cyan,"Use -c, --cloud-access to list custom access","\n"
255
272
  end
256
- print as_pretty_table(rows, [:name, :access], options)
273
+ print reset,"\n"
257
274
  else
258
- print cyan,"Use -c, --cloud-access to list custom access","\n"
275
+ # print "\n"
276
+ # print cyan,bold,"Cloud Access: #{get_access_string(json_response['globalZoneAccess'])}",reset,"\n"
259
277
  end
260
- print reset,"\n"
261
- else
262
- # print "\n"
263
- # print cyan,bold,"Cloud Access: #{get_access_string(json_response['globalZoneAccess'])}",reset,"\n"
264
278
  end
265
279
 
266
280
  print cyan
@@ -326,7 +340,7 @@ EOT
326
340
  end
327
341
  print as_pretty_table(rows, [:name, :access], options)
328
342
  else
329
- print cyan,"Use -b, --catalog-item-type-access to list custom access","\n"
343
+ print cyan,"Use --catalog-item-type-access to list custom access","\n"
330
344
  end
331
345
  else
332
346
  # print "\n"
@@ -335,7 +349,8 @@ EOT
335
349
 
336
350
 
337
351
  persona_permissions = json_response['personaPermissions'] || json_response['personas'] || []
338
- if options[:include_catalog_item_types_access]
352
+ # if options[:include_personas_access]
353
+ if persona_permissions
339
354
  print_h2 "Persona Access", options
340
355
  rows = persona_permissions.collect do |it|
341
356
  {
@@ -552,7 +567,7 @@ EOT
552
567
  options = {}
553
568
  params = {}
554
569
  optparse = Morpheus::Cli::OptionParser.new do |opts|
555
- opts.banner = subcommand_usage("[name] [options]")
570
+ opts.banner = subcommand_usage("[role] [options]")
556
571
  build_option_type_options(opts, options, update_role_option_types)
557
572
  build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote])
558
573
  end
@@ -630,7 +645,7 @@ EOT
630
645
  def remove(args)
631
646
  options = {}
632
647
  optparse = Morpheus::Cli::OptionParser.new do |opts|
633
- opts.banner = subcommand_usage("[name]")
648
+ opts.banner = subcommand_usage("[role]")
634
649
  build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :remote])
635
650
  end
636
651
  optparse.parse!(args)
@@ -770,41 +785,46 @@ EOT
770
785
  group_name = nil
771
786
  access_value = nil
772
787
  do_all = false
788
+ allowed_access_values = ['full', 'read', 'none']
773
789
  optparse = Morpheus::Cli::OptionParser.new do |opts|
774
- opts.banner = subcommand_usage("[name]")
790
+ opts.banner = subcommand_usage("[role] [group] [access]")
775
791
  opts.on( '-g', '--group GROUP', "Group name or id" ) do |val|
776
792
  group_name = val
777
793
  end
778
794
  opts.on( nil, '--all', "Update all groups at once." ) do
779
795
  do_all = true
780
796
  end
781
- opts.on( '--access VALUE', String, "Access value [full|read|none]" ) do |val|
797
+ opts.on( '--access VALUE', String, "Access value [#{allowed_access_values.join('|')}]" ) do |val|
782
798
  access_value = val
783
799
  end
784
800
  build_common_options(opts, options, [:json, :dry_run, :remote])
785
801
  opts.footer = "Update role access for a group or all groups.\n" +
786
- "[name] is required. This is the name or id of a role.\n" +
802
+ "[role] is required. This is the name or id of a role.\n" +
787
803
  "--group or --all is required. This is the name or id of a group.\n" +
788
- "--access is required. This is the new access value."
804
+ "--access is required. This is the new access value. #{anded_list(allowed_access_values)}"
789
805
  end
790
806
  optparse.parse!(args)
791
- if args.count < 1
792
- puts optparse
793
- return 1
794
- end
795
- name = args[0]
796
- # support old usage: [name] [group] [access]
797
- group_name ||= args[1]
798
- access_value ||= args[2]
799
807
 
800
- if (!group_name && !do_all) || !access_value
801
- puts optparse
802
- return 1
808
+ # usage: update-group-access [role] [access] --all
809
+ # update-group-access [role] [group] [access]
810
+ name = args[0]
811
+ if do_all
812
+ verify_args!(args:args, optparse:optparse, min:1, max:2)
813
+ access_value = args[1] if args[1]
814
+ else
815
+ verify_args!(args:args, optparse:optparse, min:1, max:3)
816
+ group_id = args[1] if args[1]
817
+ access_value = args[2] if args[2]
818
+ end
819
+ if !group_id && !do_all
820
+ raise_command_error("missing required argument: [group] or --all", optparse)
821
+ end
822
+ if !access_value
823
+ raise_command_error("missing required argument: [access]", optparse)
803
824
  end
804
-
805
825
  access_value = access_value.to_s.downcase
806
-
807
- if !['full', 'read', 'none'].include?(access_value)
826
+ if !allowed_access_values.include?(access_value)
827
+ raise_command_error("invalid access value: #{access_value}", optparse)
808
828
  puts optparse
809
829
  return 1
810
830
  end
@@ -915,6 +935,7 @@ EOT
915
935
  cloud_name = nil
916
936
  access_value = nil
917
937
  do_all = false
938
+ allowed_access_values = ['full', 'read', 'none']
918
939
  optparse = Morpheus::Cli::OptionParser.new do |opts|
919
940
  opts.banner = subcommand_usage("[name]")
920
941
  opts.on( '-c', '--cloud CLOUD', "Cloud name or id" ) do |val|
@@ -924,7 +945,7 @@ EOT
924
945
  opts.on( nil, '--all', "Update all clouds at once." ) do
925
946
  do_all = true
926
947
  end
927
- opts.on( '--access VALUE', String, "Access value [full|read|none]" ) do |val|
948
+ opts.on( '--access VALUE', String, "Access value [#{allowed_access_values.join('|')}]" ) do |val|
928
949
  access_value = val
929
950
  end
930
951
  opts.on( '-g', '--group GROUP', "Group to find cloud in" ) do |val|
@@ -932,32 +953,34 @@ EOT
932
953
  end
933
954
  build_common_options(opts, options, [:json, :dry_run, :remote])
934
955
  opts.footer = "Update role access for a cloud or all clouds.\n" +
935
- "[name] is required. This is the name or id of a role.\n" +
956
+ "[role] is required. This is the name or id of a role.\n" +
936
957
  "--cloud or --all is required. This is the name or id of a cloud.\n" +
937
- "--access is required. This is the new access value."
958
+ "--access is required. This is the new access value. #{anded_list(allowed_access_values)}"
938
959
  end
939
960
  optparse.parse!(args)
940
961
 
941
- if args.count < 1
942
- puts optparse
943
- return 1
944
- end
962
+ # usage: update-cloud-access [role] [access] --all
963
+ # update-cloud-access [role] [cloud] [access]
945
964
  name = args[0]
946
- # support old usage: [name] [cloud] [access]
947
- cloud_name ||= args[1]
948
- access_value ||= args[2]
949
-
950
- if (!cloud_name && !do_all) || !access_value
951
- puts optparse
952
- return 1
965
+ if do_all
966
+ verify_args!(args:args, optparse:optparse, min:1, max:2)
967
+ access_value = args[1] if args[1]
968
+ else
969
+ verify_args!(args:args, optparse:optparse, min:1, max:3)
970
+ cloud_id = args[1] if args[1]
971
+ access_value = args[2] if args[2]
972
+ end
973
+ if !cloud_id && !do_all
974
+ raise_command_error("missing required argument: [cloud] or --all", optparse)
975
+ end
976
+ if !access_value
977
+ raise_command_error("missing required argument: [access]", optparse)
953
978
  end
954
- puts "cloud_name is : #{cloud_name}"
955
- puts "access_value is : #{access_value}"
956
979
  access_value = access_value.to_s.downcase
957
-
958
- if !['full', 'none'].include?(access_value)
980
+ if !allowed_access_values.include?(access_value)
981
+ raise_command_error("invalid access value: #{access_value}", optparse)
959
982
  puts optparse
960
- exit 1
983
+ return 1
961
984
  end
962
985
 
963
986
  connect(options)
@@ -1025,10 +1048,10 @@ EOT
1025
1048
  end
1026
1049
 
1027
1050
  def update_global_instance_type_access(args)
1028
- usage = "Usage: morpheus roles update-global-instance-type-access [name] [full|custom|none]"
1051
+ usage = "Usage: morpheus roles update-global-instance-type-access [role] [full|custom|none]"
1029
1052
  options = {}
1030
1053
  optparse = Morpheus::Cli::OptionParser.new do |opts|
1031
- opts.banner = subcommand_usage("[name] [full|custom|none]")
1054
+ opts.banner = subcommand_usage("[role] [full|custom|none]")
1032
1055
  build_common_options(opts, options, [:json, :dry_run, :remote])
1033
1056
  end
1034
1057
  optparse.parse!(args)
@@ -1077,22 +1100,23 @@ EOT
1077
1100
  instance_type_name = nil
1078
1101
  access_value = nil
1079
1102
  do_all = false
1103
+ allowed_access_values = ['full', 'none']
1080
1104
  optparse = Morpheus::Cli::OptionParser.new do |opts|
1081
- opts.banner = subcommand_usage("[name]")
1105
+ opts.banner = subcommand_usage("[role] [type] [access]")
1082
1106
  opts.on( '--instance-type INSTANCE_TYPE', String, "Instance Type name" ) do |val|
1083
1107
  instance_type_name = val
1084
1108
  end
1085
1109
  opts.on( nil, '--all', "Update all instance types at once." ) do
1086
1110
  do_all = true
1087
1111
  end
1088
- opts.on( '--access VALUE', String, "Access value [full|read|none]" ) do |val|
1112
+ opts.on( '--access VALUE', String, "Access value [#{allowed_access_values.join('|')}]" ) do |val|
1089
1113
  access_value = val
1090
1114
  end
1091
1115
  build_common_options(opts, options, [:json, :dry_run, :remote])
1092
1116
  opts.footer = "Update role access for an instance type or all instance types.\n" +
1093
- "[name] is required. This is the name or id of a role.\n" +
1117
+ "[role] is required. This is the name or id of a role.\n" +
1094
1118
  "--instance-type or --all is required. This is the name of an instance type.\n" +
1095
- "--access is required. This is the new access value."
1119
+ "--access is required. This is the new access value. #{anded_list(allowed_access_values)}"
1096
1120
  end
1097
1121
  optparse.parse!(args)
1098
1122
 
@@ -1101,7 +1125,7 @@ EOT
1101
1125
  return 1
1102
1126
  end
1103
1127
  name = args[0]
1104
- # support old usage: [name] [instance-type] [access]
1128
+ # support old usage: [role] [instance-type] [access]
1105
1129
  instance_type_name ||= args[1]
1106
1130
  access_value ||= args[2]
1107
1131
 
@@ -1170,10 +1194,10 @@ EOT
1170
1194
  end
1171
1195
 
1172
1196
  def update_global_blueprint_access(args)
1173
- usage = "Usage: morpheus roles update-global-blueprint-access [name] [full|custom|none]"
1197
+ usage = "Usage: morpheus roles update-global-blueprint-access [role] [full|custom|none]"
1174
1198
  options = {}
1175
1199
  optparse = Morpheus::Cli::OptionParser.new do |opts|
1176
- opts.banner = subcommand_usage("[name] [full|custom|none]")
1200
+ opts.banner = subcommand_usage("[role] [full|custom|none]")
1177
1201
  build_common_options(opts, options, [:json, :dry_run, :remote])
1178
1202
  end
1179
1203
  optparse.parse!(args)
@@ -1222,42 +1246,46 @@ EOT
1222
1246
  blueprint_id = nil
1223
1247
  access_value = nil
1224
1248
  do_all = false
1249
+ allowed_access_values = ['full', 'none']
1225
1250
  optparse = Morpheus::Cli::OptionParser.new do |opts|
1226
- opts.banner = subcommand_usage("[name]")
1251
+ opts.banner = subcommand_usage("[role] [blueprint] [access]")
1227
1252
  opts.on( '--blueprint ID', String, "Blueprint ID or Name" ) do |val|
1228
1253
  blueprint_id = val
1229
1254
  end
1230
1255
  opts.on( nil, '--all', "Update all blueprints at once." ) do
1231
1256
  do_all = true
1232
1257
  end
1233
- opts.on( '--access VALUE', String, "Access value [full|read|none]" ) do |val|
1258
+ opts.on( '--access VALUE', String, "Access value [#{allowed_access_values.join('|')}]" ) do |val|
1234
1259
  access_value = val
1235
1260
  end
1236
1261
  build_common_options(opts, options, [:json, :dry_run, :remote])
1237
1262
  opts.footer = "Update role access for an blueprint or all blueprints.\n" +
1238
- "[name] is required. This is the name or id of a role.\n" +
1263
+ "[role] is required. This is the name or id of a role.\n" +
1239
1264
  "--blueprint or --all is required. This is the name or id of a blueprint.\n" +
1240
- "--access is required. This is the new access value."
1265
+ "--access is required. This is the new access value. #{anded_list(allowed_access_values)}"
1241
1266
  end
1242
1267
  optparse.parse!(args)
1243
1268
 
1244
- if args.count < 1
1245
- puts optparse
1246
- return 1
1247
- end
1269
+ # usage: update-blueprint-access [role] [access] --all
1270
+ # update-blueprint-access [role] [blueprint] [access]
1248
1271
  name = args[0]
1249
- # support old usage: [name] [blueprint] [access]
1250
- blueprint_id ||= args[1]
1251
- access_value ||= args[2]
1252
-
1253
- if (!blueprint_id && !do_all) || !access_value
1254
- puts_error optparse
1255
- return 1
1272
+ if do_all
1273
+ verify_args!(args:args, optparse:optparse, min:1, max:2)
1274
+ access_value = args[1] if args[1]
1275
+ else
1276
+ verify_args!(args:args, optparse:optparse, min:1, max:3)
1277
+ blueprint_id = args[1] if args[1]
1278
+ access_value = args[2] if args[2]
1279
+ end
1280
+ if !blueprint_id && !do_all
1281
+ raise_command_error("missing required argument: [blueprint] or --all", optparse)
1282
+ end
1283
+ if !access_value
1284
+ raise_command_error("missing required argument: [access]", optparse)
1256
1285
  end
1257
-
1258
1286
  access_value = access_value.to_s.downcase
1259
-
1260
- if !['full', 'none'].include?(access_value)
1287
+ if !allowed_access_values.include?(access_value)
1288
+ raise_command_error("invalid access value: #{access_value}", optparse)
1261
1289
  puts optparse
1262
1290
  return 1
1263
1291
  end
@@ -1327,10 +1355,10 @@ EOT
1327
1355
  end
1328
1356
 
1329
1357
  def update_global_catalog_item_type_access(args)
1330
- usage = "Usage: morpheus roles update-global-catalog-item-type-access [name] [full|custom|none]"
1358
+ usage = "Usage: morpheus roles update-global-catalog-item-type-access [role] [full|custom|none]"
1331
1359
  options = {}
1332
1360
  optparse = Morpheus::Cli::OptionParser.new do |opts|
1333
- opts.banner = subcommand_usage("[name] [full|custom|none]")
1361
+ opts.banner = subcommand_usage("[role] [full|custom|none]")
1334
1362
  build_common_options(opts, options, [:json, :dry_run, :remote])
1335
1363
  end
1336
1364
  optparse.parse!(args)
@@ -1379,42 +1407,46 @@ EOT
1379
1407
  catalog_item_type_id = nil
1380
1408
  access_value = nil
1381
1409
  do_all = false
1410
+ allowed_access_values = ['full', 'none']
1382
1411
  optparse = Morpheus::Cli::OptionParser.new do |opts|
1383
- opts.banner = subcommand_usage("[name]")
1412
+ opts.banner = subcommand_usage("[role] [catalog-item-type] [access]")
1384
1413
  opts.on( '--catalog-item-type ID', String, "Catalog Item Type ID or Name" ) do |val|
1385
1414
  catalog_item_type_id = val
1386
1415
  end
1387
1416
  opts.on( nil, '--all', "Update all catalog item types at once." ) do
1388
1417
  do_all = true
1389
1418
  end
1390
- opts.on( '--access VALUE', String, "Access value [full|none]" ) do |val|
1419
+ opts.on( '--access VALUE', String, "Access value [#{allowed_access_values.join('|')}]" ) do |val|
1391
1420
  access_value = val
1392
1421
  end
1393
1422
  build_common_options(opts, options, [:json, :dry_run, :remote])
1394
1423
  opts.footer = "Update role access for an catalog item type or all types.\n" +
1395
- "[name] is required. This is the name or id of a role.\n" +
1424
+ "[role] is required. This is the name or id of a role.\n" +
1396
1425
  "--catalog-item-type or --all is required. This is the name or id of a catalog item type.\n" +
1397
- "--access is required. This is the new access value."
1426
+ "--access is required. This is the new access value. #{anded_list(allowed_access_values)}"
1398
1427
  end
1399
1428
  optparse.parse!(args)
1400
1429
 
1401
- if args.count < 1
1402
- puts optparse
1403
- return 1
1404
- end
1430
+ # usage: update-catalog_item_type-access [role] [access] --all
1431
+ # update-catalog_item_type-access [role] [catalog-item-type] [access]
1405
1432
  name = args[0]
1406
- # support old usage: [name] [catalog_item_type] [access]
1407
- catalog_item_type_id ||= args[1]
1408
- access_value ||= args[2]
1409
-
1410
- if (!catalog_item_type_id && !do_all) || !access_value
1411
- puts_error optparse
1412
- return 1
1433
+ if do_all
1434
+ verify_args!(args:args, optparse:optparse, min:1, max:2)
1435
+ access_value = args[1] if args[1]
1436
+ else
1437
+ verify_args!(args:args, optparse:optparse, min:1, max:3)
1438
+ catalog_item_type_id = args[1] if args[1]
1439
+ access_value = args[2] if args[2]
1440
+ end
1441
+ if !catalog_item_type_id && !do_all
1442
+ raise_command_error("missing required argument: [catalog-item-type] or --all", optparse)
1443
+ end
1444
+ if !access_value
1445
+ raise_command_error("missing required argument: [access]", optparse)
1413
1446
  end
1414
-
1415
1447
  access_value = access_value.to_s.downcase
1416
-
1417
- if !['full', 'none'].include?(access_value)
1448
+ if !allowed_access_values.include?(access_value)
1449
+ raise_command_error("invalid access value: #{access_value}", optparse)
1418
1450
  puts optparse
1419
1451
  return 1
1420
1452
  end
@@ -1484,44 +1516,49 @@ EOT
1484
1516
  def update_persona_access(args)
1485
1517
  options = {}
1486
1518
  persona_id = nil
1519
+ name = nil
1487
1520
  access_value = nil
1488
1521
  do_all = false
1522
+ allowed_access_values = ['full', 'none']
1489
1523
  optparse = Morpheus::Cli::OptionParser.new do |opts|
1490
- opts.banner = subcommand_usage("[name] [serviceCatalog|standard]")
1524
+ opts.banner = subcommand_usage("[role] [persona] [access]")
1491
1525
  opts.on( '--persona CODE', String, "Persona Code" ) do |val|
1492
1526
  persona_id = val
1493
1527
  end
1494
1528
  opts.on( nil, '--all', "Update all personas at once." ) do
1495
1529
  do_all = true
1496
1530
  end
1497
- opts.on( '--access VALUE', String, "Access value [full|none]" ) do |val|
1531
+ opts.on( '--access VALUE', String, "Access value [#{allowed_access_values.join('|')}]" ) do |val|
1498
1532
  access_value = val
1499
1533
  end
1500
1534
  build_common_options(opts, options, [:json, :dry_run, :remote])
1501
- opts.footer = "Update role access for an persona or personas.\n" +
1502
- "[name] is required. This is the name or id of a role.\n" +
1503
- "--persona or --all is required. This is the code of a persona.\n" +
1504
- "--access is required. This is the new access value."
1535
+ opts.footer = "Update role access for a persona or all personas.\n" +
1536
+ "[role] is required. This is the name or id of a role.\n" +
1537
+ "--persona or --all is required. This is the code of a persona. Service Catalog or Standard\n" +
1538
+ "--access is required. This is the new access value. #{anded_list(allowed_access_values)}"
1505
1539
  end
1506
1540
  optparse.parse!(args)
1507
1541
 
1508
- if args.count < 1
1509
- puts optparse
1510
- return 1
1511
- end
1542
+ # usage: update-persona-access [role] [access] --all
1543
+ # update-persona-access [role] [persona] [access]
1512
1544
  name = args[0]
1513
- # support old usage: [name] [persona] [access]
1514
- persona_id ||= args[1]
1515
- access_value ||= args[2]
1516
-
1517
- if (!persona_id && !do_all) || !access_value
1518
- puts_error optparse
1519
- return 1
1545
+ if do_all
1546
+ verify_args!(args:args, optparse:optparse, min:1, max:2)
1547
+ access_value = args[1] if args[1]
1548
+ else
1549
+ verify_args!(args:args, optparse:optparse, min:1, max:3)
1550
+ persona_id = args[1] if args[1]
1551
+ access_value = args[2] if args[2]
1552
+ end
1553
+ if !persona_id && !do_all
1554
+ raise_command_error("missing required argument: [persona] or --all", optparse)
1555
+ end
1556
+ if !access_value
1557
+ raise_command_error("missing required argument: [access]", optparse)
1520
1558
  end
1521
-
1522
1559
  access_value = access_value.to_s.downcase
1523
-
1524
- if !['full', 'none'].include?(access_value)
1560
+ if !allowed_access_values.include?(access_value)
1561
+ raise_command_error("invalid access value: #{access_value}", optparse)
1525
1562
  puts optparse
1526
1563
  return 1
1527
1564
  end
@@ -1573,12 +1610,13 @@ EOT
1573
1610
 
1574
1611
  def add_role_option_types
1575
1612
  [
1576
- {'fieldName' => 'authority', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'displayOrder' => 1},
1577
- {'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text', 'displayOrder' => 2},
1578
- {'fieldName' => 'roleType', 'fieldLabel' => 'Role Type', 'type' => 'select', 'selectOptions' => [{'name' => 'User Role', 'value' => 'user'}, {'name' => 'Account Role', 'value' => 'account'}], 'defaultValue' => 'user', 'displayOrder' => 3},
1579
- {'fieldName' => 'baseRole', 'fieldLabel' => 'Copy From Role', 'type' => 'text', 'displayOrder' => 4},
1580
- {'fieldName' => 'multitenant', 'fieldLabel' => 'Multitenant', 'type' => 'checkbox', 'defaultValue' => 'off', 'description' => 'A Multitenant role is automatically copied into all existing subaccounts as well as placed into a subaccount when created. Useful for providing a set of predefined roles a Customer can use', 'displayOrder' => 5},
1581
- {'fieldName' => 'multitenantLocked', 'fieldLabel' => 'Multitenant Locked', 'type' => 'checkbox', 'defaultValue' => 'off', 'description' => 'Prevents subtenants from branching off this role/modifying it. ', 'displayOrder' => 6}
1613
+ {'fieldName' => 'authority', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true},
1614
+ {'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text'},
1615
+ {'fieldName' => 'roleType', 'fieldLabel' => 'Role Type', 'type' => 'select', 'selectOptions' => [{'name' => 'User Role', 'value' => 'user'}, {'name' => 'Account Role', 'value' => 'account'}], 'defaultValue' => 'user'},
1616
+ {'fieldName' => 'baseRole', 'fieldLabel' => 'Copy From Role', 'type' => 'text'},
1617
+ {'fieldName' => 'multitenant', 'fieldLabel' => 'Multitenant', 'type' => 'checkbox', 'defaultValue' => 'off', 'description' => 'A Multitenant role is automatically copied into all existing subaccounts as well as placed into a subaccount when created. Useful for providing a set of predefined roles a Customer can use'},
1618
+ {'fieldName' => 'multitenantLocked', 'fieldLabel' => 'Multitenant Locked', 'type' => 'checkbox', 'defaultValue' => 'off', 'description' => 'Prevents subtenants from branching off this role/modifying it. '},
1619
+ {'fieldName' => 'defaultPersona', 'fieldLabel' => 'Default Persona', 'type' => 'select', 'selectOptions' => [{'name'=>'Service Catalog','value'=>'serviceCatalog'},{'name'=>'Standard','value'=>'standard'}], 'description' => 'Default Persona'}
1582
1620
  ]
1583
1621
  end
1584
1622