morpheus-cli 5.4.0 → 5.4.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 (75) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/lib/morpheus/api/account_users_interface.rb +68 -0
  4. data/lib/morpheus/api/api_client.rb +51 -9
  5. data/lib/morpheus/api/audit_interface.rb +9 -0
  6. data/lib/morpheus/api/instances_interface.rb +21 -0
  7. data/lib/morpheus/api/load_balancer_monitors_interface.rb +9 -0
  8. data/lib/morpheus/api/load_balancer_pools_interface.rb +4 -4
  9. data/lib/morpheus/api/load_balancer_profiles_interface.rb +4 -5
  10. data/lib/morpheus/api/load_balancer_virtual_servers_interface.rb +13 -4
  11. data/lib/morpheus/api/load_balancers_interface.rb +5 -0
  12. data/lib/morpheus/api/network_routers_interface.rb +9 -0
  13. data/lib/morpheus/api/network_static_routes_interface.rb +36 -0
  14. data/lib/morpheus/api/read_interface.rb +4 -3
  15. data/lib/morpheus/api/rest_interface.rb +3 -3
  16. data/lib/morpheus/api/secondary_read_interface.rb +1 -1
  17. data/lib/morpheus/api/secondary_rest_interface.rb +19 -19
  18. data/lib/morpheus/api/storage_server_types_interface.rb +14 -0
  19. data/lib/morpheus/api/storage_servers_interface.rb +9 -0
  20. data/lib/morpheus/api/storage_volume_types_interface.rb +9 -0
  21. data/lib/morpheus/api/storage_volumes_interface.rb +9 -0
  22. data/lib/morpheus/api/users_interface.rb +16 -63
  23. data/lib/morpheus/cli/cli_command.rb +253 -5
  24. data/lib/morpheus/cli/cli_registry.rb +1 -1
  25. data/lib/morpheus/cli/commands/alias_command.rb +1 -1
  26. data/lib/morpheus/cli/commands/apps.rb +14 -78
  27. data/lib/morpheus/cli/commands/audit.rb +188 -0
  28. data/lib/morpheus/cli/commands/blueprints_command.rb +1 -1
  29. data/lib/morpheus/cli/commands/change_password_command.rb +4 -4
  30. data/lib/morpheus/cli/commands/clusters.rb +37 -12
  31. data/lib/morpheus/cli/commands/hosts.rb +15 -15
  32. data/lib/morpheus/cli/commands/instances.rb +109 -2
  33. data/lib/morpheus/cli/commands/load_balancer_monitors.rb +71 -0
  34. data/lib/morpheus/cli/commands/load_balancer_pools.rb +30 -50
  35. data/lib/morpheus/cli/commands/load_balancer_profiles.rb +65 -0
  36. data/lib/morpheus/cli/commands/load_balancer_types.rb +9 -4
  37. data/lib/morpheus/cli/commands/load_balancer_virtual_servers.rb +77 -57
  38. data/lib/morpheus/cli/commands/load_balancers.rb +93 -6
  39. data/lib/morpheus/cli/commands/network_firewalls_command.rb +22 -5
  40. data/lib/morpheus/cli/commands/network_routers_command.rb +96 -45
  41. data/lib/morpheus/cli/commands/network_static_routes_command.rb +446 -0
  42. data/lib/morpheus/cli/commands/network_transport_zones_command.rb +4 -4
  43. data/lib/morpheus/cli/commands/open_command.rb +30 -0
  44. data/lib/morpheus/cli/commands/options.rb +98 -0
  45. data/lib/morpheus/cli/commands/policies_command.rb +1 -1
  46. data/lib/morpheus/cli/commands/prices_command.rb +7 -7
  47. data/lib/morpheus/cli/commands/remote.rb +4 -2
  48. data/lib/morpheus/cli/commands/roles.rb +1 -1
  49. data/lib/morpheus/cli/commands/shell.rb +2 -2
  50. data/lib/morpheus/cli/commands/storage_server_types.rb +50 -0
  51. data/lib/morpheus/cli/commands/storage_servers.rb +122 -0
  52. data/lib/morpheus/cli/commands/storage_volume_types.rb +50 -0
  53. data/lib/morpheus/cli/commands/storage_volumes.rb +103 -0
  54. data/lib/morpheus/cli/commands/tenants_command.rb +1 -1
  55. data/lib/morpheus/cli/commands/user_groups_command.rb +1 -1
  56. data/lib/morpheus/cli/commands/user_settings_command.rb +2 -1
  57. data/lib/morpheus/cli/commands/user_sources_command.rb +1 -1
  58. data/lib/morpheus/cli/commands/users.rb +28 -28
  59. data/lib/morpheus/cli/commands/view.rb +102 -0
  60. data/lib/morpheus/cli/mixins/accounts_helper.rb +5 -5
  61. data/lib/morpheus/cli/mixins/load_balancers_helper.rb +24 -4
  62. data/lib/morpheus/cli/mixins/print_helper.rb +50 -18
  63. data/lib/morpheus/cli/mixins/processes_helper.rb +1 -2
  64. data/lib/morpheus/cli/mixins/provisioning_helper.rb +15 -5
  65. data/lib/morpheus/cli/mixins/rest_command.rb +145 -73
  66. data/lib/morpheus/cli/mixins/secondary_rest_command.rb +174 -81
  67. data/lib/morpheus/cli/mixins/storage_servers_helper.rb +156 -0
  68. data/lib/morpheus/cli/mixins/storage_volumes_helper.rb +119 -0
  69. data/lib/morpheus/cli/option_types.rb +45 -24
  70. data/lib/morpheus/cli/version.rb +1 -1
  71. data/lib/morpheus/cli.rb +1 -0
  72. data/lib/morpheus/ext/string.rb +29 -6
  73. data/lib/morpheus/routes.rb +238 -0
  74. data/lib/morpheus/util.rb +6 -1
  75. metadata +29 -8
@@ -0,0 +1,102 @@
1
+ require 'morpheus/cli/cli_command'
2
+ # require 'morpheus/routes'
3
+
4
+ class Morpheus::Cli::View
5
+ include Morpheus::Cli::CliCommand
6
+
7
+ set_command_description "Open the remote appliance in a web browser"
8
+ set_command_name :'view'
9
+
10
+ def connect(opts)
11
+ @api_client = establish_remote_appliance_connection(opts)
12
+ end
13
+
14
+ def handle(args)
15
+ params = {}
16
+ options = {}
17
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
18
+ opts.banner = subcommand_usage("[path] [id]")
19
+ # debate: should login using /login/ouath-redirect
20
+ opts.on('-L', '--login', "Login with the CLI access token before loading the path." ) do
21
+ options[:login] = true
22
+ end
23
+ opts.on('--absolute', "Absolute path, do not search for a matching route to use") do
24
+ options[:absolute_path] = true
25
+ end
26
+ build_common_options(opts, options, [:dry_run, :remote])
27
+ opts.footer = <<-EOT
28
+ View the remote appliance in a web browser.
29
+ [path] is optional. This the path or resource type to load. The default is the index page "/".
30
+ [id] is optional. This is the resource name or id to be append to the path to load details of a specific object.
31
+ The [path] is matched against the #{prog_name} UI site map to find the best matching route.
32
+ Route matching is skipped if the path begins with a "/" or --absolute is used.
33
+ By default no authentication is not done and the existing web browser session used.
34
+ The --login option will authenticate via the CLI access token and create a new browser session.
35
+
36
+ Examples:
37
+ view --login
38
+ view monitoring
39
+ view user 1
40
+ view user administrator
41
+ view /infrastructure/clouds/2
42
+ EOT
43
+ end
44
+ optparse.parse!(args)
45
+ # verify_args!(args:args, optparse:optparse, min: 0, max: 2)
46
+ connect(options)
47
+ # todo: it would actually be cool to use the params and include them on the path..
48
+ # params.merge!(parse_query_options(options))
49
+ path, *ids = args
50
+ # default to index page "/"
51
+ path = path || "/"
52
+ if options[:absolute_path] != true
53
+ if path.start_with?("/")
54
+ # treat like absolute path, no lookup
55
+ else
56
+ # lookup best matching route from sitemap
57
+ # lookup plural routes first, so 'app' finds apps and not approvals
58
+ found_route = Morpheus::Routes.lookup(path)
59
+ if found_route
60
+ # Morpheus::Logging::DarkPrinter.puts "Found matching route: '#{path}' => '#{found_route}'" if Morpheus::Logging.debug?
61
+ path = found_route
62
+ else
63
+ # just use specified path
64
+ end
65
+ end
66
+ # always add a leading slash
67
+ path = path.start_with?("/") ? path : "/#{path}"
68
+ # append id(s) to path if passed
69
+ if ids.size > 0
70
+ # convert names to ids
71
+ # assume the last part of path is the type and use generic finder
72
+ # only lookup names, and allow any id
73
+ ids = ids.collect do |id|
74
+ if id.to_s !~ /\A\d{1,}\Z/
75
+ # assume the last part of path is the type
76
+ record_type = path.split("/").last
77
+ record = find_by_name(record_type, id)
78
+ if record.nil?
79
+ raise_command_error("[id] is invalid. No #{record_type} found for '#{id}'", args, optparse)
80
+ end
81
+ record['id'].to_s
82
+ else
83
+ id
84
+ end
85
+ end
86
+ path = "#{path}/" + ids.join("/")
87
+ end
88
+ end
89
+ # build the link to use, either our path or oauth-redirect to that path
90
+ link = "#{@appliance_url}#{path}"
91
+ if options[:login]
92
+ # uh, this should need CGI::escape(path)
93
+ link = "#{@appliance_url}/login/oauth-redirect?access_token=#{@access_token}\\&redirectUri=#{path}"
94
+ end
95
+ if options[:dry_run]
96
+ print_system_command_dry_run(Morpheus::Util.open_url_command(link), options)
97
+ return 0, nil
98
+ end
99
+ return Morpheus::Util.open_url(link)
100
+ end
101
+
102
+ end
@@ -15,10 +15,10 @@ module Morpheus::Cli::AccountsHelper
15
15
  @accounts_interface
16
16
  end
17
17
 
18
- def users_interface
18
+ def account_users_interface
19
19
  # @api_client.users
20
- raise "#{self.class} has not defined @users_interface" if @users_interface.nil?
21
- @users_interface
20
+ raise "#{self.class} has not defined @account_users_interface" if @account_users_interface.nil?
21
+ @account_users_interface
22
22
  end
23
23
 
24
24
  def user_groups_interface
@@ -244,7 +244,7 @@ module Morpheus::Cli::AccountsHelper
244
244
 
245
245
  def find_user_by_id(account_id, id, params={})
246
246
  begin
247
- json_response = users_interface.get(account_id, id.to_i, params)
247
+ json_response = account_users_interface.get(account_id, id.to_i, params)
248
248
  return json_response['user']
249
249
  rescue RestClient::Exception => e
250
250
  if e.response && e.response.code == 404
@@ -256,7 +256,7 @@ module Morpheus::Cli::AccountsHelper
256
256
  end
257
257
 
258
258
  def find_user_by_username(account_id, username, params={})
259
- users = users_interface.list(account_id, params.merge({username: username.to_s}))['users']
259
+ users = account_users_interface.list(account_id, params.merge({username: username.to_s}))['users']
260
260
  if users.empty?
261
261
  print_red_alert "User not found by username #{username}"
262
262
  return nil
@@ -105,12 +105,32 @@ module Morpheus::Cli::LoadBalancersHelper
105
105
  end
106
106
  end
107
107
 
108
- def load_balancer_type_for_id(id)
109
- return get_available_load_balancer_types().find { |z| z['id'].to_i == id.to_i}
108
+ def load_balancer_type_for_id(val)
109
+ record = get_available_load_balancer_types().find { |z| z['id'].to_i == val.to_i}
110
+ label = "Load Balancer Type"
111
+ if record.nil?
112
+ print_red_alert "#{label.downcase} not found by id #{val}"
113
+ return nil
114
+ end
115
+ return record
110
116
  end
111
117
 
112
- def load_balancer_type_for_name(name)
113
- return get_available_load_balancer_types().find { |z| z['name'].downcase == name.downcase || z['code'].downcase == name.downcase}
118
+ def load_balancer_type_for_name(val)
119
+ records = get_available_load_balancer_types().select { |z| z['name'].downcase == val.downcase || z['code'].downcase == val.downcase}
120
+ label = "Load Balancer Type"
121
+ if records.empty?
122
+ print_red_alert "#{label} not found by name '#{val}'"
123
+ return nil
124
+ elsif records.size > 1
125
+ print_red_alert "More than one #{label.downcase} found by name '#{val}'"
126
+ print_error "\n"
127
+ puts_error as_pretty_table(records, [:id, :name], {color:red})
128
+ print_red_alert "Try using ID instead"
129
+ print_error reset,"\n"
130
+ return nil
131
+ else
132
+ return records[0]
133
+ end
114
134
  end
115
135
 
116
136
  def find_load_balancer_type_by_name_or_id(val)
@@ -202,7 +202,7 @@ module Morpheus::Cli::PrintHelper
202
202
  if options[:outfile]
203
203
  print_result = print_to_file(output, options[:outfile], options[:overwrite])
204
204
  # with_stdout_to_file(options[:outfile], options[:overwrite]) { print output }
205
- print "#{cyan}Wrote output to file #{options[:outfile]} (#{format_bytes File.size(options[:outfile])})\n" unless options[:quiet]
205
+ print "#{cyan}Wrote output to file #{options[:outfile]} (#{format_bytes(File.size(options[:outfile]))})\n" unless options[:quiet]
206
206
  #return print_result
207
207
  return
208
208
  end
@@ -220,6 +220,14 @@ module Morpheus::Cli::PrintHelper
220
220
  return
221
221
  end
222
222
 
223
+ def print_system_command_dry_run(cmd, options={})
224
+ print "\n"
225
+ print "#{cyan}#{bold}#{dark}SYSTEM COMMAND#{reset}\n"
226
+ print cmd
227
+ print reset, "\n"
228
+ return
229
+ end
230
+
223
231
  def format_api_request(http_method, url, headers, payload=nil, options={})
224
232
  out = ""
225
233
  # out << "\n"
@@ -385,33 +393,57 @@ module Morpheus::Cli::PrintHelper
385
393
  if json_response.nil? || json_response.empty?
386
394
  return ""
387
395
  end
388
-
389
- # options = OpenStruct.new(options) # laff, let's do this instead
390
396
  color = options.key?(:color) ? options[:color] : cyan
391
397
  label = options[:label]
392
- n_label = options[:n_label]
393
- # label = n_label if !label && n_label
398
+ n_label = options[:n_label] || (label ? label.to_s.pluralize : nil)
394
399
  message = options[:message] || "Viewing %{start_index}-%{end_index} of %{total} %{label}"
395
400
  blank_message = options[:blank_message] || nil # "No %{label} found"
396
401
 
397
- # support lazy passing of common json_response {"meta": {"size": {25}, "total": 56} }
398
- # otherwise use the root values given
399
- meta = OpenStruct.new(json_response)
400
- if meta.meta
401
- meta = OpenStruct.new(meta.meta)
402
- end
403
- offset, size, total = meta.offset.to_i, meta.size.to_i, meta.total.to_i
404
- #objects = meta.objects || options[:objects_key] ? json_response[options[:objects_key]] : nil
405
- #objects ||= meta.instances || meta.servers || meta.users || meta.roles
406
- #size = objects.size if objects && size == 0
407
- if total == 0
402
+ # support lazy passing of common list json_response {"meta": {"size": 25, "total": 56} }
403
+ # priority is:
404
+ # 1. "meta" that api response contains for list endpoints
405
+ # 2. "total" and "size" values if passed explicitely by the cli (pretty sure symbols are no longer used)
406
+ # 3. examine the first array found in the response
407
+ meta = nil
408
+ records = nil
409
+ # assume records is the first array in the response
410
+ records_key = json_response.keys.find { |k| json_response[k].is_a?(Array) }
411
+ if records_key
412
+ records = json_response[records_key]
413
+ meta = {'offset' => 0, 'size' => records.size, 'total' => records.size}
414
+ end
415
+ if json_response[:meta] || json_response["meta"]
416
+ meta = json_response[:meta] || json_response["meta"]
417
+ elsif json_response.key?('size') || json_response.key?('total')
418
+ meta = json_response
419
+ elsif json_response.key?(:size) || json_response.key?(:total)
420
+ meta = {'size' => json_response[:size], 'total' => json_response[:total], 'offset' => json_response[:offset]}
421
+ elsif records
422
+ # just use the first key in the response
423
+ meta = {'size' => records.size, 'total' => records.size}
424
+ end
425
+ # did not find pagination meta info?
426
+ if meta.nil?
427
+ return ""
428
+ end
429
+ # api should not need to return the size, just use records.size
430
+ if meta["size"].nil? && records
431
+ meta["size"] = records.size
432
+ end
433
+ offset = meta['offset'].to_i
434
+ size = meta['size'].to_i
435
+ total = meta['total'].to_i
436
+ # perhaps no total count returned, let total be equal to size of list
437
+ if total == 0 && size > 0
408
438
  total = size
409
439
  end
440
+ # plural label?
410
441
  if total != 1
411
- label = n_label || label
442
+ label = n_label
412
443
  end
413
444
  out_str = ""
414
- string_key_values = {start_index: format_number(offset + 1), end_index: format_number(offset + size), total: format_number(total), size: format_number(size), offset: format_number(offset), label: label}
445
+ string_key_values = {start_index: format_number(offset + 1), end_index: format_number(offset + size),
446
+ total: format_number(total), size: format_number(size), offset: format_number(offset), label: label}
415
447
  if size > 0
416
448
  if message
417
449
  out_str << message % string_key_values
@@ -1,8 +1,7 @@
1
1
  require 'morpheus/cli/mixins/print_helper'
2
2
 
3
3
  # Mixin for Morpheus::Cli command classes
4
- # Provides common methods for fetching and printing accounts, roles, and users.
5
- # The including class must establish @accounts_interface, @roles_interface, @users_interface
4
+ # Provides common methods for viewing process history
6
5
  module Morpheus::Cli::ProcessesHelper
7
6
 
8
7
  def self.included(klass)
@@ -57,9 +57,9 @@ module Morpheus::Cli::ProvisioningHelper
57
57
  @api_client.accounts
58
58
  end
59
59
 
60
- def get_available_groups(refresh=false)
60
+ def get_available_groups(params = {}, refresh=false)
61
61
  if !@available_groups || refresh
62
- option_results = options_interface.options_for_source('groups',{})
62
+ option_results = options_interface.options_for_source('groups', params)
63
63
  @available_groups = option_results['data'].collect {|it|
64
64
  {"id" => it["value"], "name" => it["name"], "value" => it["value"]}
65
65
  }
@@ -68,9 +68,9 @@ module Morpheus::Cli::ProvisioningHelper
68
68
  return @available_groups
69
69
  end
70
70
 
71
- def get_available_clouds(group_id, refresh=false)
71
+ def get_available_clouds(group_id, params = {}, refresh=false)
72
72
  if !group_id
73
- option_results = options_interface.options_for_source('clouds', {'default' => 'false'})
73
+ option_results = options_interface.options_for_source('clouds', params.merge({'default' => 'false'}))
74
74
  return option_results['data'].collect {|it|
75
75
  {"id" => it["value"], "name" => it["name"], "value" => it["value"], "zoneTypeId" => it["zoneTypeId"]}
76
76
  }
@@ -80,7 +80,7 @@ module Morpheus::Cli::ProvisioningHelper
80
80
  return []
81
81
  end
82
82
  if !group["clouds"] || refresh
83
- option_results = options_interface.options_for_source('clouds', {groupId: group_id})
83
+ option_results = options_interface.options_for_source('clouds', params.merge({groupId: group_id}))
84
84
  group["clouds"] = option_results['data'].collect {|it|
85
85
  {"id" => it["value"], "name" => it["name"], "value" => it["value"], "zoneTypeId" => it["zoneTypeId"]}
86
86
  }
@@ -942,6 +942,7 @@ module Morpheus::Cli::ProvisioningHelper
942
942
  # prompt for option types
943
943
  api_params['config'] = payload['config'] if payload['config']
944
944
  api_params['poolId'] = payload['config']['resourcePoolId'] if payload['config'] && payload['config']['resourcePoolId']
945
+ api_params['resourcePoolId'] = api_params['poolId']
945
946
 
946
947
  # set option type defaults from config
947
948
  if options[:default_config]
@@ -2093,6 +2094,15 @@ module Morpheus::Cli::ProvisioningHelper
2093
2094
  permissions
2094
2095
  end
2095
2096
 
2097
+ def prompt_permissions_v2(options, excludes = [])
2098
+ perms = prompt_permissions(options, excludes)
2099
+ rtn = {}
2100
+
2101
+ rtn['visibility'] = perms['resourcePool']['visibility'] if !perms['resourcePool'].nil?
2102
+ rtn['tenants'] = ((perms['tenantPermissions'] || {})['accounts'] || []).collect {|it| {'id' => it}}
2103
+ rtn
2104
+ end
2105
+
2096
2106
  def print_permissions(permissions, excludes = [])
2097
2107
  if permissions.nil?
2098
2108
  print_h2 "Permissions"