morpheus-cli 4.2.8 → 4.2.10

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 (88) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/lib/morpheus/api.rb +1 -1
  4. data/lib/morpheus/api/activity_interface.rb +9 -0
  5. data/lib/morpheus/api/api_client.rb +83 -27
  6. data/lib/morpheus/api/apps_interface.rb +21 -0
  7. data/lib/morpheus/api/dashboard_interface.rb +5 -21
  8. data/lib/morpheus/api/instances_interface.rb +3 -10
  9. data/lib/morpheus/api/invoice_line_items_interface.rb +14 -0
  10. data/lib/morpheus/api/invoices_interface.rb +7 -12
  11. data/lib/morpheus/api/library_layouts_interface.rb +8 -0
  12. data/lib/morpheus/api/ping_interface.rb +20 -0
  13. data/lib/morpheus/api/projects_interface.rb +33 -0
  14. data/lib/morpheus/api/setup_interface.rb +19 -36
  15. data/lib/morpheus/api/user_settings_interface.rb +0 -6
  16. data/lib/morpheus/api/whoami_interface.rb +4 -8
  17. data/lib/morpheus/benchmarking.rb +16 -26
  18. data/lib/morpheus/cli.rb +10 -5
  19. data/lib/morpheus/cli/access_token_command.rb +5 -8
  20. data/lib/morpheus/cli/activity_command.rb +146 -0
  21. data/lib/morpheus/cli/apps.rb +312 -121
  22. data/lib/morpheus/cli/archives_command.rb +1 -1
  23. data/lib/morpheus/cli/auth_command.rb +4 -11
  24. data/lib/morpheus/cli/blueprints_command.rb +196 -137
  25. data/lib/morpheus/cli/change_password_command.rb +1 -1
  26. data/lib/morpheus/cli/cli_command.rb +225 -72
  27. data/lib/morpheus/cli/cli_registry.rb +2 -2
  28. data/lib/morpheus/cli/cloud_datastores_command.rb +1 -1
  29. data/lib/morpheus/cli/clouds.rb +5 -20
  30. data/lib/morpheus/cli/clusters.rb +4 -28
  31. data/lib/morpheus/cli/commands/standard/alias_command.rb +2 -9
  32. data/lib/morpheus/cli/commands/standard/benchmark_command.rb +2 -0
  33. data/lib/morpheus/cli/commands/standard/curl_command.rb +2 -3
  34. data/lib/morpheus/cli/commands/standard/history_command.rb +3 -6
  35. data/lib/morpheus/cli/commands/standard/man_command.rb +10 -7
  36. data/lib/morpheus/cli/commands/standard/ssl_verification_command.rb +10 -9
  37. data/lib/morpheus/cli/containers_command.rb +3 -3
  38. data/lib/morpheus/cli/credentials.rb +13 -16
  39. data/lib/morpheus/cli/error_handler.rb +18 -12
  40. data/lib/morpheus/cli/errors.rb +45 -0
  41. data/lib/morpheus/cli/execute_schedules_command.rb +1 -1
  42. data/lib/morpheus/cli/execution_request_command.rb +4 -4
  43. data/lib/morpheus/cli/groups.rb +84 -132
  44. data/lib/morpheus/cli/hosts.rb +6 -16
  45. data/lib/morpheus/cli/instances.rb +100 -183
  46. data/lib/morpheus/cli/invoices_command.rb +505 -71
  47. data/lib/morpheus/cli/library_layouts_command.rb +254 -166
  48. data/lib/morpheus/cli/library_option_lists_command.rb +0 -87
  49. data/lib/morpheus/cli/library_option_types_command.rb +0 -96
  50. data/lib/morpheus/cli/license.rb +3 -0
  51. data/lib/morpheus/cli/login.rb +17 -37
  52. data/lib/morpheus/cli/logout.rb +9 -5
  53. data/lib/morpheus/cli/mixins/accounts_helper.rb +83 -7
  54. data/lib/morpheus/cli/mixins/operations_helper.rb +41 -0
  55. data/lib/morpheus/cli/mixins/option_source_helper.rb +255 -0
  56. data/lib/morpheus/cli/mixins/print_helper.rb +18 -4
  57. data/lib/morpheus/cli/mixins/provisioning_helper.rb +222 -13
  58. data/lib/morpheus/cli/mixins/remote_helper.rb +139 -0
  59. data/lib/morpheus/cli/monitoring_checks_command.rb +11 -3
  60. data/lib/morpheus/cli/network_groups_command.rb +8 -2
  61. data/lib/morpheus/cli/option_types.rb +1 -1
  62. data/lib/morpheus/cli/ping.rb +252 -0
  63. data/lib/morpheus/cli/price_sets_command.rb +16 -27
  64. data/lib/morpheus/cli/prices_command.rb +34 -27
  65. data/lib/morpheus/cli/processes_command.rb +81 -7
  66. data/lib/morpheus/cli/projects_command.rb +607 -0
  67. data/lib/morpheus/cli/recent_activity_command.rb +87 -65
  68. data/lib/morpheus/cli/remote.rb +965 -974
  69. data/lib/morpheus/cli/reports_command.rb +3 -15
  70. data/lib/morpheus/cli/roles.rb +8 -31
  71. data/lib/morpheus/cli/service_plans_command.rb +25 -31
  72. data/lib/morpheus/cli/setup.rb +392 -0
  73. data/lib/morpheus/cli/shell.rb +144 -56
  74. data/lib/morpheus/cli/subnets_command.rb +71 -11
  75. data/lib/morpheus/cli/tasks.rb +3 -3
  76. data/lib/morpheus/cli/user_sources_command.rb +4 -4
  77. data/lib/morpheus/cli/users.rb +135 -109
  78. data/lib/morpheus/cli/version.rb +1 -1
  79. data/lib/morpheus/cli/whitelabel_settings_command.rb +7 -7
  80. data/lib/morpheus/cli/whoami.rb +90 -129
  81. data/lib/morpheus/cli/wiki_command.rb +2 -14
  82. data/lib/morpheus/ext/rest_client.rb +36 -0
  83. data/lib/morpheus/formatters.rb +42 -5
  84. data/lib/morpheus/rest_client.rb +0 -10
  85. data/lib/morpheus/terminal.rb +41 -1
  86. data/lib/morpheus/util.rb +24 -0
  87. metadata +16 -3
  88. data/lib/morpheus/cli/command_error.rb +0 -22
@@ -17,26 +17,17 @@ class Morpheus::Cli::Whoami
17
17
 
18
18
  # no subcommands, just show()
19
19
 
20
- def initialize()
21
- # @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
22
- end
23
-
24
- def connect(opts)
25
- #@api_client = establish_remote_appliance_connection(opts)
26
- # begin
27
- @api_client = establish_remote_appliance_connection(opts.merge({:no_prompt => true, :skip_verify_access_token => true}))
28
- @groups_interface = @api_client.groups
29
- @active_group_id = Morpheus::Cli::Groups.active_groups[@appliance_name]
30
- # rescue Morpheus::Cli::CommandError => err
31
- # puts_error err
32
- # end
20
+ def connect(options)
21
+ # @api_client = establish_remote_appliance_connection(options)
22
+ @api_client = establish_remote_appliance_connection(options.merge({:skip_verify_access_token => true, :skip_login => true}))
23
+ @whoami_interface = @api_client.whoami
33
24
  end
34
25
 
35
26
  def handle(args)
36
- show(args)
27
+ get(args)
37
28
  end
38
29
 
39
- def show(args)
30
+ def get(args)
40
31
  options = {}
41
32
  params = {}
42
33
  username_only = false
@@ -46,12 +37,22 @@ class Morpheus::Cli::Whoami
46
37
  opts.on( '-n', '--name', "Print only your username." ) do
47
38
  username_only = true
48
39
  end
49
- opts.on('-a','--all', "Display All Details (Feature Access)") do
40
+ opts.on('-a','--all', "Display All Details") do
50
41
  options[:include_feature_access] = true
42
+ options[:include_group_access] = true
43
+ options[:include_cloud_access] = true
44
+ options[:include_instance_type_access] = true
51
45
  end
52
- opts.on('-f','--feature-access', "Display Feature Access") do
46
+ opts.on('-p','--permissions', "Display Permissions") do
53
47
  options[:include_feature_access] = true
48
+ # options[:include_group_access] = true
49
+ # options[:include_cloud_access] = true
50
+ # options[:include_instance_type_access] = true
54
51
  end
52
+ # opts.on('-f','--feature-access', "Display Feature Access") do
53
+ # options[:include_feature_access] = true
54
+ # end
55
+ # opts.add_hidden_option('--feature-access')
55
56
  # these are things that morpheus users get has to display...
56
57
  # opts.on(nil,'--group-access', "Display Group Access") do
57
58
  # options[:include_group_access] = true
@@ -62,63 +63,60 @@ class Morpheus::Cli::Whoami
62
63
  # opts.on(nil,'--instance-type-access', "Display Instance Type Access") do
63
64
  # options[:include_instance_type_access] = true
64
65
  # end
65
- opts.on('-a','--all-access', "Display All Access Lists") do
66
- options[:include_feature_access] = true
67
- options[:include_group_access] = true
68
- options[:include_cloud_access] = true
69
- options[:include_instance_type_access] = true
70
- end
71
66
  opts.on('-t','--token-only', "Print your access token only") do
72
67
  access_token_only = true
73
68
  end
74
- build_common_options(opts, options, [:json, :remote, :dry_run, :quiet])
69
+ opts.on('--offline', '--offline', "Do this offline without an api request to refresh the remote appliance status.") do
70
+ options[:do_offline] = true
71
+ end
72
+ build_standard_get_options(opts, options)
73
+ opts.footer = <<-EOT
74
+ View information about the current user.
75
+ EOT
75
76
  end
76
77
  optparse.parse!(args)
78
+ verify_args!(args:args, optparse:optparse, count:0)
77
79
  connect(options)
78
80
  begin
79
- # check to see if they have credentials instead of just trying to connect (and prompting)
80
-
81
- if !@appliance_name
82
- # never gets here..
83
- #raise_command_error "Please specify a Morpheus Appliance with -r or see the command `remote use`"
84
- print yellow,"Please specify a Morpheus Appliance with -r or see `remote use`.#{reset}\n"
85
- return 1
81
+ if @access_token.nil?
82
+ print_error yellow,"You are not currently logged in",reset,"\n"
83
+ return 1, "no current user"
86
84
  end
87
-
85
+ @whoami_interface.setopts(options)
88
86
  if options[:dry_run]
89
- print_dry_run @api_client.whoami.setopts(options).dry.get(params)
87
+ print_dry_run @whoami_interface.dry.get(params)
90
88
  return 0
91
89
  end
92
90
 
93
- # todo: fix potential issue here, should be able to use --remote-url or --username
94
- # in which case you do not have to be logged in (saved credentials)...
95
- # maybe just update connect() to do @api_client = establish_remote_appliance_connection(opts)
96
- # wallet = Morpheus::Cli::Credentials.new(@appliance_name, @appliance_url).request_credentials(options)
97
- wallet = Morpheus::Cli::Credentials.new(@appliance_name, @appliance_url).load_saved_credentials()
98
- token = wallet ? wallet['access_token'] : nil
99
- if !token
100
- if options[:quiet]
101
- return 1
102
- elsif access_token_only
103
- puts_error "(logged out)" # stderr probably
104
- return 1
105
- else
106
- print yellow,"You are not currently logged in to #{display_appliance(@appliance_name, @appliance_url)}",reset,"\n"
107
- print yellow,"Use the 'login' command.",reset,"\n"
108
- return 1
109
- end
110
- end
111
-
112
91
  #json_response = load_whoami()
113
-
114
- whoami_interface = @api_client.whoami.setopts(options)
115
- whoami_response = whoami_interface.get()
92
+ whoami_response = nil
93
+ if options[:do_offline]
94
+ # if @remote_appliance && @remote_appliance[:username]
95
+ # exit_code = 0
96
+ # else
97
+ # exit_code = 1
98
+ # end
99
+ # no permissions, or even name stored atm, we should start storing that.
100
+ # then we can start checking permissions nd restricting command visibility.
101
+ whoami_response = {
102
+ "user": {
103
+ "username" => @remote_appliance ? @remote_appliance[:username] : nil
104
+ },
105
+ # "isMasterAccount" => true,
106
+ "permissions" => [],
107
+ "appliance" => {
108
+ "buildVersion" => @remote_appliance ? @remote_appliance[:build_version] : nil
109
+ }
110
+ }
111
+ else
112
+ whoami_response = @whoami_interface.get(params)
113
+ end
116
114
  json_response = whoami_response
117
115
  @current_user = whoami_response["user"]
118
- if @current_user.empty?
119
- print_red_alert "Unauthenticated. Please login."
120
- exit 1
121
- end
116
+ # if @current_user.nil?
117
+ # print_red_alert "Unauthenticated. Please login."
118
+ # exit 1
119
+ # end
122
120
  @is_master_account = whoami_response["isMasterAccount"]
123
121
  @user_permissions = whoami_response["permissions"]
124
122
 
@@ -128,62 +126,52 @@ class Morpheus::Cli::Whoami
128
126
  @appliance_build_verison = nil
129
127
  end
130
128
 
131
- if access_token_only
132
- if options[:quiet]
133
- return @current_user ? 0 : 1
134
- end
135
- if @access_token.nil?
136
- print yellow,"\n","No access token. Please login",reset,"\n"
137
- return false
138
- end
139
- print cyan,@access_token.to_s,reset,"\n"
140
- return 0
141
- end
129
+ render_response(json_response, options) do
142
130
 
143
- if username_only
144
- if options[:quiet]
145
- return @current_user ? 0 : 1
146
- end
147
- if @current_user.nil?
148
- puts_error "(logged out)" # "(anonymous)" || ""
149
- return 1
150
- else
151
- print cyan,@current_user['username'].to_s,reset,"\n"
131
+ if access_token_only
132
+ if options[:quiet]
133
+ return @current_user ? 0 : 1
134
+ end
135
+ if @access_token.nil?
136
+ print yellow,"\n","No access token. Please login",reset,"\n"
137
+ return false
138
+ end
139
+ print cyan,@access_token.to_s,reset,"\n"
152
140
  return 0
153
141
  end
154
- end
155
142
 
156
-
157
- active_group = nil
158
- begin
159
- active_group = @active_group_id ? find_group_by_name_or_id(@active_group_id) : nil # via InfrastructureHelper mixin
160
- rescue => err
161
- if options[:debug]
162
- print red,"Unable to determine active group: #{err}\n",reset
143
+ if username_only
144
+ if options[:quiet]
145
+ return @current_user ? 0 : 1
146
+ end
147
+ if @current_user.nil?
148
+ puts_error "(logged out)" # "(anonymous)" || ""
149
+ return 1
150
+ else
151
+ print cyan,@current_user['username'].to_s,reset,"\n"
152
+ return 0
153
+ end
163
154
  end
164
- end
165
155
 
166
- if options[:json]
167
- print JSON.pretty_generate(json_response)
168
- print "\n"
169
- else
170
156
  if @current_user.nil?
171
157
  print yellow,"\n","No active session. Please login",reset,"\n"
172
158
  exit 1
173
159
  end
174
-
175
- print_h1 "Current User", options
160
+ subtitles = []
161
+ #subtitles << "#{display_appliance(@appliance_name, @appliance_url)}"
162
+ print_h1 "Current User", subtitles, options
176
163
  print cyan
177
164
  print_description_list({
178
165
  "ID" => 'id',
179
- "Account" => lambda {|it| (it['account'] ? it['account']['name'] : '') + (@is_master_account ? " (Master Account)" : '') },
180
- # "First Name" => 'firstName',
181
- # "Last Name" => 'lastName',
166
+ "Tenant" => lambda {|it| (it['account'] ? it['account']['name'] : '') + (@is_master_account ? " (Master Account)" : '') },
167
+ "First Name" => 'firstName',
168
+ "Last Name" => 'lastName',
182
169
  # "Name" => 'displayName',
183
- "Name" => lambda {|it| it['firstName'] ? it['displayName'] : '' },
170
+ #"Name" => lambda {|it| it['firstName'] ? it['displayName'] : '' },
184
171
  "Username" => 'username',
185
172
  "Email" => 'email',
186
- "Role" => lambda {|it| format_user_role_names(it) }
173
+ "Role" => lambda {|it| format_user_role_names(it) },
174
+ #"Remote" => lambda {|it| display_appliance(@appliance_name, @appliance_url) },
187
175
  }, @current_user)
188
176
  print cyan
189
177
 
@@ -196,12 +184,12 @@ class Morpheus::Cli::Whoami
196
184
  if @user_permissions.is_a?(Hash)
197
185
  # api used to return map like [code:access]
198
186
  rows = @user_permissions.collect do |code, access|
199
- {permission: code, access: get_access_string(access) }
187
+ {permission: code, access: format_access_string(access) }
200
188
  end
201
189
  else
202
190
  # api now returns an array of objects like [[name:"Foo",code:"foo",access:"full"], ...]
203
191
  rows = @user_permissions.collect do |it|
204
- {permission: (it['name'] || it['code']), access: get_access_string(it['access']) }
192
+ {permission: (it['name'] || it['code']), access: format_access_string(it['access']) }
205
193
  end
206
194
  end
207
195
  # api sort sux right now
@@ -215,34 +203,7 @@ class Morpheus::Cli::Whoami
215
203
  end
216
204
  end
217
205
 
218
- print_h1 "Remote Appliance", options
219
- print cyan
220
- appliance_data = {
221
- 'name' => @appliance_name,
222
- 'url' => @appliance_url,
223
- 'buildVersion' => @appliance_build_verison
224
- }
225
- print_description_list({
226
- "Name" => 'name',
227
- "URL" => 'url',
228
- "Version" => 'buildVersion'
229
- }, appliance_data)
230
-
231
- if active_group
232
- print cyan
233
- # print_h1 "Active Group", options
234
- # print cyan
235
- # print_description_list({
236
- # "ID" => 'id',
237
- # "Name" => 'name'
238
- # }, active_group)
239
- print cyan, "\n# => Currently using group #{active_group['name']}\n", reset
240
- print reset,"\n"
241
- else
242
- print "\n", reset
243
- print cyan, "No active group. See `groups use`\n",reset
244
- print reset,"\n"
245
- end
206
+ print reset, "\n"
246
207
 
247
208
  # save pertinent session info to the appliance
248
209
  begin
@@ -173,23 +173,11 @@ class Morpheus::Cli::WikiCommand
173
173
 
174
174
  link = "#{@appliance_url}/login/oauth-redirect?access_token=#{@access_token}\\&redirectUri=/operations/wiki/#{page['urlName']}"
175
175
 
176
- open_command = nil
177
- if RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/
178
- open_command = "start #{link}"
179
- elsif RbConfig::CONFIG['host_os'] =~ /darwin/
180
- open_command = "open #{link}"
181
- elsif RbConfig::CONFIG['host_os'] =~ /linux|bsd/
182
- open_command = "xdg-open #{link}"
183
- end
184
-
185
176
  if options[:dry_run]
186
- puts "system: #{open_command}"
177
+ puts Morpheus::Util.open_url_command(link)
187
178
  return 0
188
179
  end
189
-
190
- system(open_command)
191
-
192
- return 0
180
+ return Morpheus::Util.open_url(link)
193
181
  rescue RestClient::Exception => e
194
182
  print_rest_exception(e, options)
195
183
  exit 1
@@ -0,0 +1,36 @@
1
+ # this does some monkey patching of third party RestClient
2
+ # to modify the logging output a bit
3
+ require 'rest-client'
4
+
5
+ module RestClient
6
+ class Request
7
+
8
+ def log_request
9
+ begin
10
+ return unless RestClient.log
11
+ out = []
12
+ # out << "RestClient.#{method} #{redacted_url.inspect}"
13
+ out << "#{method.to_s.upcase} #{redacted_url.inspect}"
14
+ out << payload.short_inspect if payload
15
+ out << processed_headers.to_a.sort.map { |(k, v)| [k.inspect, v.inspect].join("=>") }.join(", ")
16
+ RestClient.log << out.join(', ') + "\n"
17
+ rescue => ex
18
+ # something went wrong, wrong gem version maybe...above is from rest-client 2.0.2
19
+ # do it the old way
20
+ super
21
+ end
22
+ end
23
+ =begin
24
+ def log_response res
25
+ return unless RestClient.log
26
+ size = if @raw_response
27
+ File.size(@tf.path)
28
+ else
29
+ res.body.nil? ? 0 : res.body.size
30
+ end
31
+
32
+ RestClient.log << "# => #{res.code} #{res.class.to_s.gsub(/^Net::HTTP/, '')} | #{(res['Content-type'] || '').gsub(/;.*$/, '')} #{size} bytes\n"
33
+ end
34
+ =end
35
+ end
36
+ end
@@ -91,6 +91,20 @@ def format_duration(start_time, end_time=nil, format="human")
91
91
  format_duration_seconds(seconds, format)
92
92
  end
93
93
 
94
+ def format_duration_ago(start_time, end_time=nil)
95
+ if !start_time
96
+ return ""
97
+ end
98
+ start_time = parse_time(start_time)
99
+ if end_time
100
+ end_time = parse_time(end_time)
101
+ else
102
+ end_time = Time.now
103
+ end
104
+ seconds = (end_time - start_time).abs
105
+ format_human_duration(seconds, true)
106
+ end
107
+
94
108
  def format_duration_seconds(seconds, format="human")
95
109
  seconds = seconds.abs
96
110
  out = ""
@@ -120,7 +134,7 @@ end
120
134
 
121
135
  # returns a human readable time duration
122
136
  # @param seconds - duration in seconds
123
- def format_human_duration(seconds)
137
+ def format_human_duration(seconds, show_relative=false)
124
138
  out = ""
125
139
  #seconds = seconds.round
126
140
  days, hours, minutes = (seconds / (60*60*24)).floor, (seconds / (60*60)).floor, (seconds / (60)).floor
@@ -152,18 +166,30 @@ def format_human_duration(seconds)
152
166
  ms = (seconds.to_f * 1000).to_i
153
167
  out << "#{ms}ms"
154
168
  else
155
- seconds = seconds.floor
156
- if seconds == 1
169
+ if seconds.floor == 1
157
170
  out << "1 second"
158
171
  else
159
- out << "#{seconds} seconds"
172
+ out << "#{seconds.floor} seconds"
173
+ end
174
+ end
175
+ if show_relative
176
+ if seconds < 1
177
+ out = "just now"
178
+ else
179
+ out << " ago"
160
180
  end
161
181
  end
162
182
  out
163
183
  end
164
184
 
165
185
  def display_appliance(name, url)
166
- "#{name} - #{url}"
186
+ if name.to_s == "" || name == 'remote-url'
187
+ # "#{url}"
188
+ "#{url}"
189
+ else
190
+ # "#{name} #{url}"
191
+ "[#{name}] #{url}"
192
+ end
167
193
  end
168
194
 
169
195
  def iso8601(dt)
@@ -321,6 +347,7 @@ def no_colors(str)
321
347
  str.to_s.gsub /\e\[\d+m/, ""
322
348
  end
323
349
 
350
+
324
351
  def format_number(n, opts={})
325
352
  delim = opts[:delimiter] || ','
326
353
  out = ""
@@ -338,6 +365,16 @@ def format_number(n, opts={})
338
365
  return out
339
366
  end
340
367
 
368
+ def format_sig_dig(n, sig_dig=3)
369
+ out = ""
370
+ parts = n.to_f.round(sig_dig).to_s.split(".")
371
+ if parts.size > 1 && sig_dig
372
+ parts[1] = parts[1].ljust(sig_dig, "0")
373
+ end
374
+ out << parts.join(".")
375
+ return out
376
+ end
377
+
341
378
  def currency_sym(currency)
342
379
  Money::Currency.new((currency || 'usd').to_sym).symbol
343
380
  end