morpheus-cli 2.10.3 → 2.11.0

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 (68) hide show
  1. checksums.yaml +4 -4
  2. data/bin/morpheus +5 -96
  3. data/lib/morpheus/api/api_client.rb +23 -1
  4. data/lib/morpheus/api/checks_interface.rb +106 -0
  5. data/lib/morpheus/api/incidents_interface.rb +102 -0
  6. data/lib/morpheus/api/monitoring_apps_interface.rb +47 -0
  7. data/lib/morpheus/api/monitoring_contacts_interface.rb +47 -0
  8. data/lib/morpheus/api/monitoring_groups_interface.rb +47 -0
  9. data/lib/morpheus/api/monitoring_interface.rb +36 -0
  10. data/lib/morpheus/api/option_type_lists_interface.rb +47 -0
  11. data/lib/morpheus/api/option_types_interface.rb +47 -0
  12. data/lib/morpheus/api/roles_interface.rb +0 -1
  13. data/lib/morpheus/api/setup_interface.rb +19 -9
  14. data/lib/morpheus/cli.rb +20 -1
  15. data/lib/morpheus/cli/accounts.rb +8 -3
  16. data/lib/morpheus/cli/app_templates.rb +19 -11
  17. data/lib/morpheus/cli/apps.rb +52 -37
  18. data/lib/morpheus/cli/cli_command.rb +229 -53
  19. data/lib/morpheus/cli/cli_registry.rb +48 -40
  20. data/lib/morpheus/cli/clouds.rb +55 -26
  21. data/lib/morpheus/cli/command_error.rb +12 -0
  22. data/lib/morpheus/cli/credentials.rb +68 -26
  23. data/lib/morpheus/cli/curl_command.rb +98 -0
  24. data/lib/morpheus/cli/dashboard_command.rb +2 -7
  25. data/lib/morpheus/cli/deployments.rb +4 -4
  26. data/lib/morpheus/cli/deploys.rb +1 -2
  27. data/lib/morpheus/cli/dot_file.rb +5 -8
  28. data/lib/morpheus/cli/error_handler.rb +179 -15
  29. data/lib/morpheus/cli/groups.rb +21 -13
  30. data/lib/morpheus/cli/hosts.rb +220 -110
  31. data/lib/morpheus/cli/instance_types.rb +2 -2
  32. data/lib/morpheus/cli/instances.rb +257 -167
  33. data/lib/morpheus/cli/key_pairs.rb +15 -9
  34. data/lib/morpheus/cli/library.rb +673 -27
  35. data/lib/morpheus/cli/license.rb +2 -2
  36. data/lib/morpheus/cli/load_balancers.rb +4 -4
  37. data/lib/morpheus/cli/log_level_command.rb +6 -4
  38. data/lib/morpheus/cli/login.rb +17 -3
  39. data/lib/morpheus/cli/logout.rb +25 -11
  40. data/lib/morpheus/cli/man_command.rb +388 -0
  41. data/lib/morpheus/cli/mixins/accounts_helper.rb +1 -1
  42. data/lib/morpheus/cli/mixins/monitoring_helper.rb +434 -0
  43. data/lib/morpheus/cli/mixins/print_helper.rb +620 -112
  44. data/lib/morpheus/cli/mixins/provisioning_helper.rb +1 -1
  45. data/lib/morpheus/cli/monitoring_apps_command.rb +29 -0
  46. data/lib/morpheus/cli/monitoring_checks_command.rb +427 -0
  47. data/lib/morpheus/cli/monitoring_contacts_command.rb +373 -0
  48. data/lib/morpheus/cli/monitoring_groups_command.rb +29 -0
  49. data/lib/morpheus/cli/monitoring_incidents_command.rb +711 -0
  50. data/lib/morpheus/cli/option_types.rb +10 -1
  51. data/lib/morpheus/cli/recent_activity_command.rb +2 -5
  52. data/lib/morpheus/cli/remote.rb +874 -134
  53. data/lib/morpheus/cli/roles.rb +54 -27
  54. data/lib/morpheus/cli/security_group_rules.rb +2 -2
  55. data/lib/morpheus/cli/security_groups.rb +23 -19
  56. data/lib/morpheus/cli/set_prompt_command.rb +50 -0
  57. data/lib/morpheus/cli/shell.rb +222 -157
  58. data/lib/morpheus/cli/tasks.rb +19 -15
  59. data/lib/morpheus/cli/users.rb +27 -17
  60. data/lib/morpheus/cli/version.rb +1 -1
  61. data/lib/morpheus/cli/virtual_images.rb +28 -13
  62. data/lib/morpheus/cli/whoami.rb +131 -52
  63. data/lib/morpheus/cli/workflows.rb +24 -9
  64. data/lib/morpheus/formatters.rb +195 -3
  65. data/lib/morpheus/logging.rb +86 -0
  66. data/lib/morpheus/terminal.rb +371 -0
  67. data/scripts/generate_morpheus_commands_help.morpheus +60 -0
  68. metadata +21 -2
@@ -32,6 +32,10 @@ module Morpheus
32
32
  end
33
33
  end
34
34
 
35
+ def self.no_prompt(option_types, options={}, api_client=nil,api_params={})
36
+ prompt(option_types, options, api_client, api_params, true)
37
+ end
38
+
35
39
  def self.prompt(option_types, options={}, api_client=nil,api_params={}, no_prompt=false)
36
40
  results = {}
37
41
  options = options || {}
@@ -210,7 +214,12 @@ module Morpheus
210
214
  select_options = option_type['selectOptions']
211
215
  # remote optionSource aka /api/options/$optionSource?
212
216
  elsif option_type['optionSource']
213
- select_options = load_source_options(option_type['optionSource'],api_client,api_params)
217
+ # /api/options/list is a special action for custom OptionTypeLists, just need to pass the optionTypeId parameter
218
+ if option_type['optionSource'] == 'list'
219
+ select_options = load_source_options(option_type['optionSource'], api_client, {'optionTypeId' => option_type['id']})
220
+ else
221
+ select_options = load_source_options(option_type['optionSource'], api_client, api_params)
222
+ end
214
223
  else
215
224
  raise "select_prompt() requires selectOptions or optionSource!"
216
225
  end
@@ -60,12 +60,9 @@ class Morpheus::Cli::RecentActivityCommand
60
60
 
61
61
  # todo: impersonate command and show that info here
62
62
 
63
- print "\n" ,cyan, bold, "Dashboard\n","==================", reset, "\n\n"
63
+ print_h1 "Recent Activity"
64
64
  print cyan
65
- print "\n"
66
- puts "Coming soon.... see --json"
67
- print "\n"
68
-
65
+ puts "Coming soon... see --json"
69
66
  print reset,"\n"
70
67
 
71
68
  end
@@ -1,7 +1,9 @@
1
1
  require 'fileutils'
2
+ require 'ostruct'
2
3
  require 'yaml'
3
4
  require 'io/console'
4
5
  require 'rest_client'
6
+ require 'net/https'
5
7
  require 'optparse'
6
8
  require 'morpheus/cli/cli_command'
7
9
 
@@ -9,7 +11,7 @@ require 'morpheus/cli/cli_command'
9
11
  class Morpheus::Cli::Remote
10
12
  include Morpheus::Cli::CliCommand
11
13
 
12
- register_subcommands :list, :add, :remove, :use, :unuse, {:current => :print_current}, :setup
14
+ register_subcommands :list, :add, :get, :update, :remove, :use, :unuse, :current, :setup, :check
13
15
  set_default_subcommand :list
14
16
 
15
17
  def initialize()
@@ -17,197 +19,592 @@ class Morpheus::Cli::Remote
17
19
  end
18
20
 
19
21
  def handle(args)
20
- if args.count == 0
21
- list(args)
22
- else
23
- handle_subcommand(args)
24
- end
22
+ # if args.count == 0
23
+ # list(args)
24
+ # else
25
+ # handle_subcommand(args)
26
+ # end
27
+ handle_subcommand(args)
25
28
  end
26
29
 
27
30
  def list(args)
28
31
  options = {}
32
+ show_all_activity = false
29
33
  optparse = Morpheus::Cli::OptionParser.new do|opts|
30
34
  opts.banner = subcommand_usage()
31
- build_common_options(opts, options, [])
32
- opts.footer = "This outputs a list of the remote appliances.\n" +
33
- "It also displays the current active appliance.\n" +
34
- "The shortcut `remote` can be used instead of `remote list`."
35
+ opts.on("-a",'--all', "Show all the appliance activity details") do
36
+ show_all_activity = true
37
+ end
38
+ build_common_options(opts, options, [:json, :csv, :fields])
39
+ opts.footer = <<-EOT
40
+ This outputs a list of the configured remote appliances. It also indicates
41
+ the current appliance. The current appliance is where morpheus will send
42
+ its commands by default. That is, in absence of the '--remote' option.
43
+ EOT
35
44
  end
36
45
  optparse.parse!(args)
37
- @appliances = ::Morpheus::Cli::Remote.appliances
38
- if @appliances == nil || @appliances.empty?
39
- print yellow,"No remote appliances configured, see `remote add`",reset,"\n"
46
+ if args.count > 0
47
+ raise ::OptionParser::NeedlessArgument.new("#{args.join(' ')}")
48
+ end
49
+ @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
50
+ appliances = ::Morpheus::Cli::Remote.load_all_remotes({})
51
+ # if appliances.empty?
52
+ # raise_command_error "You have no appliances configured. See the `remote add` command."
53
+ # end
54
+
55
+ if options[:json]
56
+ json_response = {"appliances" => appliances} # mock payload
57
+ if options[:include_fields]
58
+ json_response = {"appliances" => filter_data(appliances, options[:include_fields]) }
59
+ end
60
+ puts as_json(json_response, options)
61
+ return 0
62
+ end
63
+ if options[:csv]
64
+ puts records_as_csv(appliances, options)
65
+ return 0
66
+ end
67
+
68
+ print_h1 "Morpheus Appliances"
69
+ if appliances.empty?
70
+ print yellow
71
+ puts "You have no appliances configured. See the `remote add` command."
72
+ print reset, "\n"
40
73
  else
41
- rows = @appliances.collect do |app_name, v|
42
- {
43
- active: (v[:active] ? "=>" : ""),
44
- name: app_name,
45
- host: v[:host]
46
- }
47
- end
48
- print "\n" ,cyan, bold, "Morpheus Appliances\n","==================", reset, "\n\n"
49
74
  print cyan
50
- tp rows, {:active => {:display_name => ""}}, {:name => {:width => 16}}, {:host => {:width => 40}}
75
+ #tp rows, {:active => {:display_name => ""}}, {:name => {:width => 16}}, {:host => {:width => 40}}
76
+ columns = [
77
+ {:active => {:display_name => "", :display_method => lambda {|it| it[:active] ? "=>" : "" } } },
78
+ {:name => {:width => 16} },
79
+ {:host => {:width => 40} },
80
+ {:version => lambda {|it| it[:build_version] } },
81
+ {:status => lambda {|it| format_appliance_status(it, cyan) } },
82
+ :username,
83
+ # {:session => {display_method: lambda {|it| get_appliance_session_blurbs(it).join(' ') }, max_width: 24} }
84
+ {:activity => {display_method: lambda {|it| show_all_activity ? get_appliance_session_blurbs(it).join("\t") : get_appliance_session_blurbs(it).first } } }
85
+ ]
86
+ print as_pretty_table(appliances, columns, options)
51
87
  print reset
52
88
  if @appliance_name
53
- #unless @appliances.keys.size == 1
89
+ #unless appliances.keys.size == 1
54
90
  print cyan, "\n# => Currently using #{@appliance_name}\n", reset
55
91
  #end
56
92
  else
57
- print "\n# => No active remote appliance, see `remote use`\n", reset
93
+ print "\n# => No current remote appliance, see `remote use`\n", reset
58
94
  end
59
- print "\n" # meh
95
+ print reset, "\n"
60
96
  end
97
+ return 0, nil
61
98
  end
62
99
 
63
100
  def add(args)
101
+ exit_code, err = 0, nil
64
102
  options = {}
103
+ params = {}
104
+ new_appliance_map = {}
65
105
  use_it = false
106
+ is_insecure = nil
66
107
  optparse = Morpheus::Cli::OptionParser.new do|opts|
67
- opts.banner = subcommand_usage("[name] [url]")
68
- opts.on( '--use', '--use', "Make this the current remote appliance" ) do
108
+ banner = subcommand_usage("[name] [url]")
109
+ banner_args = <<-EOT
110
+ [name] The name for your appliance. eg. mymorph
111
+ [url] The url of your appliance eg. https://morpheus.mycompany.com
112
+ EOT
113
+ opts.banner = banner + "\n" + banner_args
114
+ opts.on(nil, '--use', "Make this the current appliance" ) do
69
115
  use_it = true
116
+ new_appliance_map[:active] = true
70
117
  end
71
118
  # let's free up the -d switch for global options, maybe?
72
119
  opts.on( '-d', '--default', "Does the same thing as --use" ) do
73
120
  use_it = true
121
+ new_appliance_map[:active] = true
122
+ end
123
+ opts.on(nil, "--secure", "Prevent insecure HTTPS communication. This is enabled by default.") do
124
+ params[:secure] = true
125
+ end
126
+ opts.on(nil, "--insecure", "Allow insecure HTTPS communication. i.e. Ignore SSL errors.") do
127
+ params[:insecure] = true
74
128
  end
75
- # todo: use Morpheus::Cli::OptionParser < OptionParser
76
- # opts.on('-h', '--help', "Prints this help" ) do
77
- # hidden_switches = ["--default"]
78
- # good_opts = opts.to_s.split("\n").delete_if { |line| hidden_switches.find {|it| line =~ /#{Regexp.escape(it)}/ } }.join("\n")
79
- # puts good_opts
80
- # exit
81
- # end
82
129
  build_common_options(opts, options, [:quiet])
83
- opts.footer = "This will add a new appliance to your list.\n" +
84
- "If it's first one, it will be made the current active appliance."
130
+ opts.footer = <<-EOT
131
+ This will add a new remote appliance to your morpheus client configuration.
132
+ If the new remote is your one and only, --use is automatically applied and
133
+ it will be made the current remote appliance.
134
+ This command will prompt you to login and/or setup a fresh appliance.
135
+ Prompting can be skipped with use of the --quiet option.
136
+ EOT
85
137
  end
86
138
  optparse.parse!(args)
87
139
  if args.count < 2
88
- puts optparse
89
- exit 1
140
+ print_error Morpheus::Terminal.angry_prompt
141
+ puts_error "#{command_name} add expects 2 arguments: [name] [url]"
142
+ puts_error optparse
143
+ return 1
90
144
  end
91
-
145
+
146
+ # load current appliances
147
+ appliances = ::Morpheus::Cli::Remote.appliances
148
+
149
+ # always use the first one
150
+ if appliances.empty?
151
+ new_appliance_map[:active] = true
152
+ end
153
+
154
+ # validate options
155
+ # construct new appliance map
156
+ # and save it in the config file
92
157
  new_appliance_name = args[0].to_sym
158
+
159
+ # for the sake of sanity
160
+ if [:current, :all].include?(new_appliance_name)
161
+ raise_command_error "The specified appliance name is invalid: '#{args[0]}'"
162
+ end
163
+ # unique name
164
+ if appliances[new_appliance_name] != nil
165
+ raise_command_error "Remote appliance already configured with the name '#{args[0]}'"
166
+ end
167
+ new_appliance_map[:name] = new_appliance_name
168
+
169
+ if params[:insecure]
170
+ new_appliance_map[:insecure] = true
171
+ elsif params[:secure]
172
+ new_appliance_map.delete(:insecure)
173
+ end
174
+ if params[:url] || params[:host]
175
+ url = params[:url] || params[:host]
176
+ end
177
+
93
178
  url = args[1]
94
- if url !~ /^https?\:\/\//
95
- print red, "The specified appliance url is invalid: '#{args[1]}'", reset, "\n"
96
- puts optparse
97
- exit 1
179
+ if url !~ /^https?\:\/\/.+/
180
+ raise_command_error "The specified appliance url is invalid: '#{args[1]}'"
181
+ #puts optparse
182
+ return 1
98
183
  end
99
- # maybe a ping here would be cool
100
- @appliances = ::Morpheus::Cli::Remote.appliances
101
- if @appliances.keys.empty?
102
- use_it = true
184
+ new_appliance_map[:host] = url
185
+
186
+ # save it
187
+ appliance = ::Morpheus::Cli::Remote.save_remote(new_appliance_name, new_appliance_map)
188
+
189
+ if !options[:quiet]
190
+ # print_green_success "Added remote #{new_appliance_name}"
191
+ print_green_success "Added remote #{new_appliance_name}"
103
192
  end
104
- if @appliances[new_appliance_name] != nil
105
- print red, "Remote appliance already configured with the name '#{args[0]}'", reset, "\n"
106
- return false
107
- else
108
- @appliances[new_appliance_name] = {
109
- host: url,
110
- active: use_it
111
- }
112
- ::Morpheus::Cli::Remote.save_appliances(@appliances)
113
- if use_it
114
- Morpheus::Cli::Remote.set_active_appliance(new_appliance_name)
115
- @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
193
+
194
+ # hit check api and store version and other info
195
+ if !options[:quiet]
196
+ print cyan
197
+ puts "Inspecting remote appliance url: #{appliance[:host]} ..."
198
+ end
199
+ appliance = ::Morpheus::Cli::Remote.refresh_remote(new_appliance_name)
200
+ if !options[:quiet]
201
+ print cyan
202
+ puts "Status is: #{format_appliance_status(appliance)}"
203
+ end
204
+ # puts "refreshed appliance #{appliance.inspect}"
205
+ # determine command exit_code and err
206
+ exit_code = (appliance[:status] == 'ready' || appliance[:status] == 'fresh') ? 0 : 1
207
+
208
+ if exit_code == 0
209
+ if appliance[:error]
210
+ exit_code = 1
211
+ err = "Check Failed: #{appliance[:error]}"
116
212
  end
117
213
  end
118
-
119
- if options[:quiet] || options[:no_prompt]
120
- return true
214
+
215
+ if options[:quiet]
216
+ return exit_code, err
121
217
  end
218
+
219
+ # check_cmd_result = check_appliance([new_appliance_name, "--quiet"])
220
+ # check_cmd_result = check_appliance([new_appliance_name])
221
+
222
+ if appliance[:status] == 'fresh' # || appliance[:setup_needed] == true
223
+ print cyan
224
+ puts "It looks like this appliance needs to be setup. Starting setup ..."
225
+ return setup([new_appliance_name])
226
+ # no need to login, setup() handles that
227
+ end
228
+
122
229
 
123
- # check to see if this is a fresh appliance.
124
- # GET /api/setup only returns 200 if it can still be initialized, else 400
125
- @setup_interface = Morpheus::SetupInterface.new(@appliance_url)
126
- appliance_status_json = nil
230
+ # only login if you are using this remote
231
+ # maybe remote use should do the login prompting eh?
232
+ # if appliance[:active] && appliance[:status] == 'ready'
233
+ if appliance[:status] == 'ready'
234
+ print reset
235
+ if ::Morpheus::Cli::OptionTypes::confirm("Would you like to login now?", options.merge({default: true}))
236
+ login_result = ::Morpheus::Cli::Login.new.handle(["--remote", appliance[:name].to_s])
237
+ keep_trying = true
238
+ while keep_trying do
239
+ if ::Morpheus::Cli::OptionTypes::confirm("Login was unsuccessful. Would you like to try again?", options.merge({default: true}))
240
+ login_result = ::Morpheus::Cli::Login.new.handle(["--remote", appliance[:name].to_s])
241
+ if login_result == 0
242
+ keep_trying = false
243
+ end
244
+ else
245
+ keep_trying = false
246
+ end
247
+ end
248
+
249
+ end
250
+ else
251
+ #puts "Status is #{format_appliance_status(appliance)}"
252
+ end
253
+
254
+ # print new appliance details
255
+ _get(appliance[:name], {})
256
+
257
+ return exit_code, err
258
+ end
259
+
260
+ def refresh(args)
261
+ check_appliance(args)
262
+ end
263
+
264
+ def check(args)
265
+ options = {}
266
+ checkall = false
267
+ optparse = Morpheus::Cli::OptionParser.new do|opts|
268
+ opts.banner = subcommand_usage("[name]")
269
+ opts.on("-a",'--all', "Refresh all appliances") do
270
+ checkall = true
271
+ end
272
+ build_common_options(opts, options, [:quiet])
273
+ opts.footer = <<-EOT
274
+ This can be used to refresh a remote appliance status.
275
+ It makes an api request to the configured appliance url, and stores the status
276
+ of the server and other info, e.g. version, in the local morpheus cli configuration.
277
+ EOT
278
+ end
279
+ optparse.parse!(args)
280
+ id_list = nil
281
+ checkall = true if args[0] == "all" and args.size == 1 # sure, why not
282
+ if checkall
283
+ # id_list = ::Morpheus::Cli::Remote.appliances.keys # sort ?
284
+ return _check_all_appliances()
285
+ elsif args.count < 1
286
+ print_error Morpheus::Terminal.angry_prompt
287
+ puts_error "#{command_name} update expects argument [name] or option --all"
288
+ puts_error optparse
289
+ return 1
290
+ else
291
+ id_list = parse_id_list(args)
292
+ end
293
+ #connect(options)
294
+ return run_command_for_each_arg(id_list) do |arg|
295
+ _check_appliance(arg, options)
296
+ end
297
+ end
298
+
299
+ def _check_all_appliances()
300
+ # reresh all appliances and then display the list view
301
+ id_list = ::Morpheus::Cli::Remote.appliances.keys # sort ?
302
+ if id_list.size > 1
303
+ print cyan
304
+ print "Checking #{id_list.size} appliances "
305
+ end
306
+ id_list.each do |appliance_name|
307
+ print "."
308
+ ::Morpheus::Cli::Remote.refresh_remote(appliance_name)
309
+ end
310
+ print "\n"
311
+ list([])
312
+
313
+ end
314
+
315
+ def _check_appliance(appliance_name, options)
127
316
  begin
128
- appliance_status_json = @setup_interface.get()
129
- if appliance_status_json['success'] == true
130
- return setup([new_appliance_name])
317
+ appliance = nil
318
+ if appliance_name == "current"
319
+ appliance = ::Morpheus::Cli::Remote.load_active_remote()
320
+ if !appliance
321
+ raise_command_error "No current appliance, see `remote use`."
322
+ end
323
+ appliance_name = appliance[:name]
324
+ else
325
+ appliance = ::Morpheus::Cli::Remote.load_remote(appliance_name)
326
+ if !appliance
327
+ raise_command_error "Remote appliance not found by the name '#{appliance_name}'"
328
+ end
131
329
  end
132
- # should not get here
330
+
331
+ # found appliance
332
+ # now refresh it
333
+
334
+ start_time = Time.now
335
+
336
+ Morpheus::Logging::DarkPrinter.puts "checking remote appliance url: #{appliance[:host]} ..." if Morpheus::Logging.debug?
337
+
338
+ appliance = ::Morpheus::Cli::Remote.refresh_remote(appliance_name)
339
+
340
+ took_sec = (Time.now - start_time)
341
+
342
+ if options[:quiet]
343
+ return 0
344
+ end
345
+
346
+ Morpheus::Logging::DarkPrinter.puts "remote appliance check completed in #{took_sec.round(3)}s" if Morpheus::Logging.debug?
347
+
348
+ # puts "remote #{appliance[:name]} status: #{format_appliance_status(appliance)}"
349
+
350
+ # if options[:json]
351
+ # print JSON.pretty_generate(json_response), "\n"
352
+ # return
353
+ # end
354
+
355
+ # show user latest info
356
+ return _get(appliance[:name], {})
357
+
133
358
  rescue RestClient::Exception => e
134
- #print_rest_exception(e, options)
135
- # laff, treating any non - 200 as meaning it is good ...is bad.. could be at the wrong site.. sending credentials..
136
- print cyan,"Appliance is ready.\n", reset
359
+ print_rest_exception(e, options)
360
+ exit 1
137
361
  end
362
+ end
138
363
 
139
- if use_it
140
- if ::Morpheus::Cli::OptionTypes::confirm("Would you like to login now?", options.merge({default: true}))
141
- return ::Morpheus::Cli::Login.new.handle([new_appliance_name])
364
+ def update(args)
365
+ options = {}
366
+ params = {}
367
+ use_it = false
368
+ is_insecure = nil
369
+ optparse = Morpheus::Cli::OptionParser.new do|opts|
370
+ opts.banner = subcommand_usage("[name]")
371
+ # opts.on(nil, "--name STRING", "Update the name of your remote appliance") do |val|
372
+ # params['name'] = val
373
+ # end
374
+ opts.on("--url URL", String, "Update the url of your remote appliance") do |val|
375
+ params[:host] = val
376
+ end
377
+ opts.on(nil, "--secure", "Prevent insecure HTTPS communication. This is enabled by default") do
378
+ params[:secure] = true
142
379
  end
380
+ opts.on(nil, "--insecure", "Allow insecure HTTPS communication. i.e. Ignore SSL errors.") do
381
+ params[:insecure] = true
382
+ end
383
+ opts.on(nil, '--use', "Make this the current appliance" ) do
384
+ use_it = true
385
+ params[:active] = true
386
+ end
387
+ build_common_options(opts, options, [:quiet])
388
+ opts.footer = "This can be used to update remote appliance settings.\n"
389
+ end
390
+ optparse.parse!(args)
391
+ puts "args is #{args.inspect}"
392
+ if args.count != 1
393
+ print_error Morpheus::Terminal.angry_prompt
394
+ puts_error "#{command_name} update expects argument [name]."
395
+ puts_error optparse
396
+ return 1
143
397
  end
144
398
 
145
- return true
399
+ appliance_name = args[0].to_sym
400
+ appliance = ::Morpheus::Cli::Remote.load_remote(appliance_name)
401
+ if !appliance
402
+ raise_command_error "Remote appliance not found by the name '#{appliance_name}'"
403
+ end
404
+
405
+ # params[:url] = args[1] if args[1]
406
+
407
+ if params.empty?
408
+ print_error Morpheus::Terminal.angry_prompt
409
+ puts_error "Specify atleast one option to update"
410
+ puts_error optparse
411
+ return 1
412
+ end
413
+
414
+ if params[:insecure]
415
+ appliance[:insecure] = true
416
+ elsif params[:secure]
417
+ appliance.delete(:insecure)
418
+ end
419
+ if params[:url] || params[:host]
420
+ appliance[:host] = params[:url] || params[:host]
421
+ end
422
+
423
+ ::Morpheus::Cli::Remote.save_remote(appliance_name, appliance)
424
+
425
+ print_green_success "Updated remote #{appliance_name}"
426
+ # todo: just go ahead and refresh it now...
427
+ # _check(appliance_name, {:quiet => true})
428
+ appliance = ::Morpheus::Cli::Remote.refresh_remote(appliance_name)
429
+ # print new appliance details
430
+ _get(appliance[:name], {})
431
+ return 0, nil
432
+ end
433
+
434
+ def get(args)
435
+ options = {}
436
+ optparse = OptionParser.new do|opts|
437
+ opts.banner = subcommand_usage("[name]")
438
+ build_common_options(opts, options, [:json,:csv, :fields, :quiet])
439
+ end
440
+ optparse.parse!(args)
441
+ if args.count < 1
442
+ print_error Morpheus::Terminal.angry_prompt
443
+ puts_error "#{command_name} get expects argument [name]."
444
+ puts_error optparse
445
+ return 1
446
+ end
447
+ #connect(options)
448
+ id_list = parse_id_list(args)
449
+ return run_command_for_each_arg(id_list) do |arg|
450
+ _get(arg, options)
451
+ end
452
+ end
453
+
454
+ def _get(appliance_name, options)
455
+ begin
456
+ appliance = nil
457
+ if appliance_name == "current"
458
+ appliance = ::Morpheus::Cli::Remote.load_active_remote()
459
+ if !appliance
460
+ raise_command_error "No current appliance, see `remote use`."
461
+ end
462
+ appliance_name = appliance[:name]
463
+ else
464
+ appliance = ::Morpheus::Cli::Remote.load_remote(appliance_name)
465
+ if !appliance
466
+ raise_command_error "Remote appliance not found by the name '#{appliance_name}'"
467
+ end
468
+ end
469
+
470
+ if options[:json]
471
+ json_response = {remote_appliance: appliance} # mock payload
472
+ puts as_json(json_response, options)
473
+ return
474
+ end
475
+
476
+ # expando
477
+ # appliance = OStruct.new(appliance)
478
+
479
+ # todo: just go ahead and refresh it now...
480
+ # _check(appliance_name, {:quiet => true})
481
+ # appliance = ::Morpheus::Cli::Remote.refresh_remote(appliance_name)
482
+
483
+ if appliance[:active]
484
+ # print_h1 "Current Remote Appliance: #{appliance[:name]}"
485
+ print_h1 "Remote Appliance: #{appliance[:name]}"
486
+ else
487
+ print_h1 "Remote Appliance: #{appliance[:name]}"
488
+ end
489
+ print cyan
490
+ description_cols = {
491
+ "Name" => :name,
492
+ "Url" => :host,
493
+ "Secure" => lambda {|it| format_appliance_secure(it) },
494
+ "Version" => lambda {|it| it[:build_version] ? "#{it[:build_version]}" : 'unknown' },
495
+ "Status" => lambda {|it| format_appliance_status(it, cyan) },
496
+ "Username" => :username,
497
+ # "Authenticated" => lambda {|it| format_boolean it[:authenticated] },
498
+ # todo: fix this layout, obv
499
+ "Activity" => lambda {|it| get_appliance_session_blurbs(it).join("\n" + (' '*10)) }
500
+ }
501
+ print cyan
502
+ puts as_description_list(appliance, description_cols)
503
+
504
+ # if appliance[:insecure]
505
+ # puts " Ignore SSL Errors: Yes"
506
+ # else
507
+ # puts " Ignore SSL Errors: No"
508
+ # end
509
+
510
+ if appliance[:active]
511
+ # print cyan
512
+ print cyan, "\n", " => This is the current appliance.", "\n"
513
+ end
514
+
515
+ print reset, "\n"
516
+
517
+ rescue RestClient::Exception => e
518
+ print_rest_exception(e, options)
519
+ exit 1
520
+ end
146
521
  end
147
522
 
148
523
  def remove(args)
149
524
  options = {}
150
525
  optparse = Morpheus::Cli::OptionParser.new do|opts|
151
526
  opts.banner = subcommand_usage("[name]")
152
- opts.on( '-d', '--default', "Make this the default remote appliance" ) do
153
- options[:default] = true
154
- end
527
+ # opts.on( '-f', '--force', "Remote appliance anyway??" ) do
528
+ # options[:default] = true
529
+ # end
155
530
  opts.footer = "This will delete an appliance from your list."
156
- build_common_options(opts, options, [:auto_confirm])
531
+ build_common_options(opts, options, [:auto_confirm, :quiet])
157
532
  end
158
533
  optparse.parse!(args)
159
- if args.empty?
160
- puts optparse
161
- exit 1
534
+ if args.count < 1
535
+ #raise_command_error "#{command_name} remove requires argument [name].", optparse
536
+ print_error Morpheus::Terminal.angry_prompt
537
+ puts_error "#{command_name} remove requires argument [name]."
538
+ puts_error optparse
539
+ return 1, nil
162
540
  end
163
- @appliances = ::Morpheus::Cli::Remote.appliances
164
541
  appliance_name = args[0].to_sym
165
- if @appliances[appliance_name] == nil
166
- print red, "Remote appliance not found by the name '#{args[0]}'", reset, "\n"
167
- else
168
- unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to remove this remote appliance '#{appliance_name}'?", options)
169
- exit 1
170
- end
171
- @appliances.delete(appliance_name)
172
- ::Morpheus::Cli::Remote.save_appliances(@appliances)
173
- # todo: also delete credentials and groups[appliance_name]
174
- ::Morpheus::Cli::Groups.clear_active_group(appliance_name) # rescue nil
175
- # this should be a class method too
176
- #::Morpheus::Cli::Credentials.clear_saved_credentials(appliance_name)
177
- ::Morpheus::Cli::Credentials.new(appliance_name, nil).clear_saved_credentials(appliance_name) # rescue nil
178
- #list([])
542
+ appliance = ::Morpheus::Cli::Remote.load_remote(appliance_name)
543
+ if !appliance
544
+ raise_command_error "Remote appliance not found by the name '#{appliance_name}'"
545
+ end
546
+ unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to delete '#{appliance_name}' from your list of remote appliances?", options)
547
+ return 9, "aborted command" # new exit code for aborting confirmation
548
+ end
549
+
550
+ # ok, delete it
551
+ ::Morpheus::Cli::Remote.delete_remote(appliance_name)
552
+
553
+ # return result
554
+ if options[:quiet]
555
+ return 0, nil
556
+ end
557
+ print_green_success "Deleted remote #{appliance_name}"
558
+ list([])
559
+ # recalculate shell prompt after this change
560
+ if Morpheus::Cli::Shell.instance
561
+ Morpheus::Cli::Shell.instance.reinitialize()
179
562
  end
563
+ return 0, nil
180
564
  end
181
565
 
182
566
  def use(args)
183
567
  options = {}
184
568
  optparse = Morpheus::Cli::OptionParser.new do|opts|
185
569
  opts.banner = subcommand_usage("[name]")
186
- build_common_options(opts, options, [])
187
- opts.footer = "This sets the current active appliance.\n" +
570
+ build_common_options(opts, options, [:quiet])
571
+ opts.footer = "Make an appliance the current remote appliance.\n" +
188
572
  "This allows you to switch between your different appliances.\n" +
189
573
  "You may override this with the --remote option in your commands."
190
574
  end
191
575
  optparse.parse!(args)
192
576
  if args.count < 1
193
- puts optparse
194
- exit 1
577
+ print_error Morpheus::Terminal.angry_prompt
578
+ puts_error "#{command_name} use expects argument [name]."
579
+ puts_error optparse
580
+ return 1
195
581
  end
196
- new_appliance_name = args[0].to_sym
197
- @appliances = ::Morpheus::Cli::Remote.appliances
198
- if @appliance_name && @appliance_name.to_s == new_appliance_name.to_s
199
- print reset,"Already using the appliance '#{args[0]}'","\n",reset
200
- else
201
- if @appliances[new_appliance_name] == nil
202
- print red, "Remote appliance not found by the name '#{args[0]}'", reset, "\n"
203
- return false
204
- else
205
- Morpheus::Cli::Remote.set_active_appliance(new_appliance_name)
206
- @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
207
- #print cyan,"Switched to using appliance #{args[0]}","\n",reset
208
- #list([])
582
+ appliance_name = args[0].to_sym
583
+ appliance = ::Morpheus::Cli::Remote.load_remote(appliance_name)
584
+ if !appliance
585
+ raise_command_error "Remote appliance not found by the name '#{appliance_name}'"
586
+ end
587
+
588
+ if appliance[:active] == true
589
+ if !options[:quiet]
590
+ print cyan
591
+ puts "Using remote #{appliance_name} (still)"
209
592
  end
593
+ return true
210
594
  end
595
+ # appliance = ::Morpheus::Cli::Remote.set_active_appliance(appliance_name)
596
+ appliance[:active] = true
597
+ appliance = ::Morpheus::Cli::Remote.save_remote(appliance_name, appliance)
598
+
599
+ # recalculate shell prompt after this change
600
+ if Morpheus::Cli::Shell.instance
601
+ Morpheus::Cli::Shell.instance.reinitialize()
602
+ end
603
+
604
+ if !options[:quiet]
605
+ puts "#{cyan}Using remote #{appliance_name}#{reset}"
606
+ end
607
+ return true
211
608
  end
212
609
 
213
610
  def unuse(args)
@@ -215,18 +612,49 @@ class Morpheus::Cli::Remote
215
612
  optparse = Morpheus::Cli::OptionParser.new do|opts|
216
613
  opts.banner = subcommand_usage()
217
614
  opts.footer = "" +
218
- "This clears the current active appliance.\n" +
615
+ "This clears the current remote appliance.\n" +
219
616
  "You will need to use an appliance, or pass the --remote option to your commands."
220
617
  build_common_options(opts, options, [])
221
618
  end
222
619
  optparse.parse!(args)
223
620
  @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
621
+ if !@appliance_name
622
+ puts "You are not using any appliance"
623
+ return false
624
+ end
625
+ Morpheus::Cli::Remote.clear_active_appliance()
626
+ puts "You are no longer using the appliance #{@appliance_name}"
627
+ # recalculate shell prompt after this change
628
+ if Morpheus::Cli::Shell.instance
629
+ Morpheus::Cli::Shell.instance.reinitialize()
630
+ end
631
+ return true
632
+ end
633
+
634
+ def current(args)
635
+ options = {}
636
+ name_only = false
637
+ optparse = Morpheus::Cli::OptionParser.new do|opts|
638
+ opts.banner = subcommand_usage()
639
+ opts.on( '-n', '--name', "Print only the name." ) do
640
+ name_only = true
641
+ end
642
+ build_common_options(opts, options, [])
643
+ opts.footer = "Print details about the current remote appliance." +
644
+ "The default behavior is the same as 'remote get current'."
645
+ end
646
+ optparse.parse!(args)
647
+
648
+ if name_only
649
+ return print_current(args)
650
+ else
651
+ return _get("current", {})
652
+ end
653
+
224
654
  if @appliance_name
225
- Morpheus::Cli::Remote.clear_active_appliance()
226
- @appliance_name, @appliance_url = nil, nil
227
- return true
655
+ print cyan, @appliance_name,"\n",reset
228
656
  else
229
- puts "You are not using any appliance"
657
+ print yellow, "No active appliance, see `remote use`\n", reset
230
658
  return false
231
659
  end
232
660
  end
@@ -235,7 +663,7 @@ class Morpheus::Cli::Remote
235
663
  options = {}
236
664
  optparse = Morpheus::Cli::OptionParser.new do|opts|
237
665
  opts.banner = subcommand_usage()
238
- build_common_options(opts, options, [:json])
666
+ build_common_options(opts, options, [])
239
667
  opts.footer = "Prints the name of the current remote appliance"
240
668
  end
241
669
  optparse.parse!(args)
@@ -300,7 +728,7 @@ class Morpheus::Cli::Remote
300
728
  # end
301
729
  return false
302
730
  else
303
- print "\n" ,cyan, bold, "Morpheus Appliance Setup", "\n", "==================", reset, "\n\n"
731
+ print_h1 "Morpheus Appliance Setup"
304
732
 
305
733
  puts "It looks like you're the first one here."
306
734
  puts "Let's initialize your remote appliance at #{@appliance_url}"
@@ -308,7 +736,7 @@ class Morpheus::Cli::Remote
308
736
 
309
737
 
310
738
  # Master Account
311
- print "\n" ,cyan, bold, "Create Master account", "\n", "==================", reset, "\n\n"
739
+ print_h2 "Create Master Account"
312
740
  account_option_types = [
313
741
  {'fieldName' => 'accountName', 'fieldLabel' => 'Master Account Name', 'type' => 'text', 'required' => true, 'displayOrder' => 1},
314
742
  ]
@@ -316,7 +744,7 @@ class Morpheus::Cli::Remote
316
744
  payload.merge!(v_prompt)
317
745
 
318
746
  # Master User
319
- print "\n" ,cyan, bold, "Create Master User", "\n", "==================", reset, "\n\n"
747
+ print_h2 "Create Master User"
320
748
  user_option_types = [
321
749
  {'fieldName' => 'firstName', 'fieldLabel' => 'First Name', 'type' => 'text', 'required' => false, 'displayOrder' => 1},
322
750
  {'fieldName' => 'lastName', 'fieldLabel' => 'Last Name', 'type' => 'text', 'required' => false, 'displayOrder' => 2},
@@ -339,7 +767,7 @@ class Morpheus::Cli::Remote
339
767
  payload.merge!(v_prompt)
340
768
 
341
769
  # Extra settings
342
- print "\n" ,cyan, bold, "Initial Setup", "\n", "==================", reset, "\n\n"
770
+ print_h2 "Initial Setup"
343
771
  extra_option_types = [
344
772
  {'fieldName' => 'applianceName', 'fieldLabel' => 'Appliance Name', 'type' => 'text', 'required' => true, 'defaultValue' => nil},
345
773
  {'fieldName' => 'applianceUrl', 'fieldLabel' => 'Appliance URL', 'type' => 'text', 'required' => true, 'defaultValue' => appliance_status_json['applianceUrl']},
@@ -401,6 +829,107 @@ class Morpheus::Cli::Remote
401
829
  end
402
830
  end
403
831
 
832
+ def format_appliance_status(app_map, return_color=cyan)
833
+ return "" if !app_map
834
+ status_str = app_map[:status] || app_map['status'] || "unknown" # get_object_value(app_map, :status)
835
+ status_str = status_str.empty? ? "unknown" : status_str.to_s.downcase
836
+ out = ""
837
+ if status_str == "new"
838
+ out << "#{cyan}#{status_str.upcase}#{return_color}"
839
+ elsif status_str == "ready"
840
+ out << "#{green}#{status_str.upcase}#{return_color}"
841
+ elsif status_str == "unreachable"
842
+ out << "#{red}#{status_str.upcase}#{return_color}"
843
+ elsif status_str.include?("error")
844
+ out << "#{red}#{status_str.upcase}#{return_color}"
845
+ # elsif status_str == "unknown"
846
+ # out << "#{yellow}#{status_str}#{return_color}"
847
+ elsif status_str == "fresh"
848
+ # cold appliance, needs setup
849
+ out << "#{magenta}#{status_str.upcase}#{return_color}"
850
+ else
851
+ # dunno
852
+ out << "#{status_str}"
853
+ end
854
+ out
855
+ end
856
+
857
+ def format_appliance_secure(app_map, return_color=cyan)
858
+ return "" if !app_map
859
+ out = ""
860
+ is_ssl = app_map[:host].to_s =~ /^https/
861
+ if !is_ssl
862
+ out << "No (no SSL)"
863
+ else
864
+ if app_map[:insecure]
865
+ out << "No (Ignore SSL Errors)"
866
+ else
867
+ # should have a flag that gets set when everything actually looks good..
868
+ out << "Yes"
869
+ end
870
+ end
871
+ out
872
+ end
873
+
874
+ # get display info about the current and past sessions
875
+ #
876
+ def get_appliance_session_blurbs(app_map)
877
+ # app_map = OStruct.new(app_map)
878
+ blurbs = []
879
+ # Current User
880
+ #
881
+ username = app_map[:username]
882
+ # creds = app_map[:access_token]
883
+ #creds = Morpheus::Cli::Credentials.new(app_map[:name], app_map[:host]).load_saved_credentials()
884
+
885
+
886
+
887
+ if app_map[:status] == 'ready'
888
+
889
+ if app_map[:authenticated]
890
+ #blurbs << app_map[:username] ? "Authenticated as #{app_map[:username]}" : "Authenticated"
891
+ blurbs << "Authenticated."
892
+ if app_map[:last_login_at]
893
+ blurbs << "Logged in #{format_duration(app_map[:last_login_at])} ago."
894
+ end
895
+ else
896
+ if app_map[:last_logout_at]
897
+ blurbs << "Logged out #{format_duration(app_map[:last_logout_at])} ago."
898
+ else
899
+ blurbs << "Logged out."
900
+ end
901
+ if app_map[:last_login_at]
902
+ blurbs << "Last login at #{format_local_dt(app_map[:last_login_at])}."
903
+ end
904
+ end
905
+
906
+ if app_map[:last_success_at]
907
+ blurbs << "Last success at #{format_local_dt(app_map[:last_success_at])}"
908
+ end
909
+
910
+ else
911
+
912
+ if app_map[:last_check]
913
+ if app_map[:last_check][:timestamp]
914
+ blurbs << "Last checked #{format_duration(app_map[:last_check][:timestamp])} ago."
915
+ end
916
+ if app_map[:last_check][:error]
917
+ blurbs << "Error: #{app_map[:last_check][:error]}"
918
+ end
919
+ if app_map[:last_check][:http_status]
920
+ blurbs << "HTTP #{app_map[:last_check][:http_status]}"
921
+ end
922
+ end
923
+
924
+ if app_map[:last_success_at]
925
+ blurbs << "Last Success: #{format_local_dt(app_map[:last_success_at])}"
926
+ end
927
+
928
+ end
929
+
930
+ return blurbs
931
+ end
932
+
404
933
  class << self
405
934
  include Term::ANSIColor
406
935
 
@@ -408,9 +937,6 @@ class Morpheus::Cli::Remote
408
937
  # it is structured like :appliance_name => {:host => "htt[://api.gomorpheus.com", :active => true}
409
938
  # not named @@appliances to avoid confusion with the instance variable . This is also a command class...
410
939
  @@appliance_config = nil
411
- #@@current_appliance = nil
412
-
413
-
414
940
 
415
941
  def appliances
416
942
  self.appliance_config
@@ -433,23 +959,87 @@ class Morpheus::Cli::Remote
433
959
  end
434
960
  end
435
961
 
436
- def set_active_appliance(name)
962
+ # Returns all the appliances in the configuration
963
+ # @param params [Hash] not used right now
964
+ # @return [Array] of appliances, all of them.
965
+ def load_all_remotes(params={})
966
+ if self.appliances.empty?
967
+ return []
968
+ end
969
+ all_appliances = self.appliances.collect do |app_name, app_map|
970
+ row = app_map.clone # OStruct.new(app_map) tempting
971
+ row[:name] = app_name
972
+ row
973
+ # {
974
+ # active: v[:active],
975
+ # name: app_name,
976
+ # host: v[:host], # || v[:url],
977
+ # #"LICENSE": v[:licenseIsInstalled] ? "Installed" : "(unknown)" # never return a license key from the server, ever!
978
+ # status: v[:status],
979
+ # username: v[:username],
980
+ # last_check: v[:last_check],
981
+ # last_whoami: v[:last_whoami],
982
+ # last_api_request: v[:last_api_request],
983
+ # last_api_result: v[:last_api_result],
984
+ # last_command: v[:last_command],
985
+ # last_command_result: v[:last_command_result]
986
+ # }
987
+ end
988
+ return all_appliances
989
+ end
990
+
991
+ # @return Hash info about the active appliance
992
+ def load_active_remote()
993
+ # todo: use this in favor of Remote.active_appliance perhaps?
994
+ if self.appliances.empty?
995
+ return nil
996
+ end
997
+ result = nil
998
+ app_name, app_map = self.appliances.find {|k,v| v[:active] == true }
999
+ if app_map
1000
+ result = app_map
1001
+ result[:name] = app_name # app_name.to_s to be more consistant with other display values
1002
+ end
1003
+ return result
1004
+ end
1005
+
1006
+ # @param [String or Symbol] name of the remote to load (converted to symbol)
1007
+ # @return [Hash] info about the appliance
1008
+ def load_remote(app_name)
1009
+ if self.appliances.empty? || app_name.nil?
1010
+ return nil
1011
+ end
1012
+ result = nil
1013
+ app_map = self.appliances[app_name.to_sym]
1014
+ if app_map
1015
+ result = app_map
1016
+ result[:name] = app_name # .to_s probably better
1017
+ end
1018
+ return result
1019
+ end
1020
+
1021
+ def set_active_appliance(app_name)
1022
+ app_name = app_name.to_sym
437
1023
  new_appliances = self.appliances
438
1024
  new_appliances.each do |k,v|
439
- is_match = (name ? (k == name.to_sym) : false)
1025
+ is_match = (app_name ? (k == app_name) : false)
440
1026
  if is_match
441
1027
  v[:active] = true
442
1028
  else
443
- v[:active] = false
1029
+ v.delete(:active)
1030
+ # v.delete('active')
1031
+ # v[:active] = false
444
1032
  end
445
1033
  end
446
1034
  save_appliances(new_appliances)
1035
+ return load_remote(app_name)
447
1036
  end
448
1037
 
449
1038
  def clear_active_appliance
1039
+ #return set_active_appliance(nil)
450
1040
  new_appliances = self.appliances
451
1041
  new_appliances.each do |k,v|
452
- v[:active] = false
1042
+ v.delete(:active)
453
1043
  end
454
1044
  save_appliances(new_appliances)
455
1045
  end
@@ -457,7 +1047,7 @@ class Morpheus::Cli::Remote
457
1047
  def load_appliance_file
458
1048
  fn = appliances_file_path
459
1049
  if File.exist? fn
460
- print "#{dark} #=> loading appliances file #{fn}#{reset}\n" if Morpheus::Logging.debug?
1050
+ Morpheus::Logging::DarkPrinter.puts "loading appliances file #{fn}" if Morpheus::Logging.debug?
461
1051
  return YAML.load_file(fn)
462
1052
  else
463
1053
  return {}
@@ -485,6 +1075,156 @@ class Morpheus::Cli::Remote
485
1075
  @@appliance_config = new_config
486
1076
  end
487
1077
 
1078
+ # save_remote updates the appliance info
1079
+ # @param app_name [Symbol] name and key for the appliance
1080
+ # @param app_map [Hash] appliance configuration data :url, :insecure, :active, :etc
1081
+ # @return [Hash] updated appliance config data
1082
+ def save_remote(app_name, app_map)
1083
+ app_name = app_name.to_sym
1084
+ # it's probably better to use load_appliance_file() here instead
1085
+ cur_appliances = self.appliances #.clone
1086
+ cur_appliances[app_name] = app_map
1087
+ cur_appliances[app_name] ||= {:status => "unknown", :error => "Bad configuration. Missing url. See 'remote update --url'" }
1088
+
1089
+ # this is the new set_active_appliance(), instead just pass :active => true
1090
+ # remove active flag from others
1091
+ if app_map[:active]
1092
+ cur_appliances.each do |k,v|
1093
+ is_match = (app_name ? (k == app_name) : false)
1094
+ if is_match
1095
+ v[:active] = true
1096
+ else
1097
+ v.delete(:active)
1098
+ # v.delete('active')
1099
+ # v[:active] = false
1100
+ end
1101
+ end
1102
+ end
1103
+
1104
+ # persist all appliances
1105
+ save_appliances(cur_appliances)
1106
+
1107
+ return app_map
1108
+ end
1109
+
1110
+ def delete_remote(app_name)
1111
+ app_name = app_name.to_sym
1112
+ cur_appliances = self.appliances #.clone
1113
+ app_map = cur_appliances[app_name]
1114
+ if !app_map
1115
+ return nil
1116
+ end
1117
+ # remove it from config and delete credentials
1118
+ cur_appliances.delete(app_name)
1119
+ ::Morpheus::Cli::Remote.save_appliances(cur_appliances)
1120
+ # this should be a class method too
1121
+ ::Morpheus::Cli::Credentials.new(app_name, nil).clear_saved_credentials(app_name)
1122
+ # delete from groups too..
1123
+ ::Morpheus::Cli::Groups.clear_active_group(app_name)
1124
+ # return the deleted value
1125
+ return app_map
1126
+ end
1127
+
1128
+ # refresh_remote makes an api request to the configured appliance url
1129
+ # and updates the appliance's build version, status and last_check attributes
1130
+ def refresh_remote(app_name)
1131
+ # this might be better off staying in the CliCommands themselves
1132
+ # todo: public api /api/setup/check should move to /api/version or /api/server-info
1133
+ app_name = app_name.to_sym
1134
+ cur_appliances = self.appliances
1135
+ app_map = cur_appliances[app_name] || {}
1136
+ app_url = app_map[:host] # || app_map[:url] maybe??
1137
+
1138
+ if !app_url
1139
+ raise "appliance config is missing url!" # should not need this
1140
+ end
1141
+
1142
+ # todo: this insecure flag needs to applied everywhere now tho..
1143
+ Morpheus::RestClient.enable_ssl_verification = app_map[:insecure].to_s == 'true'
1144
+ # Morpheus::RestClient.enable_http = app_map[:insecure].to_s == 'true'
1145
+ setup_interface = Morpheus::SetupInterface.new(app_url)
1146
+ begin
1147
+ now = Time.now.to_i
1148
+ app_map[:last_check] = {}
1149
+ app_map[:last_check][:success] = false
1150
+ app_map[:last_check][:timestamp] = Time.now.to_i
1151
+ # todo: move /api/setup/check to /api/version or /api/server-info
1152
+ check_json_response = setup_interface.check()
1153
+ # puts "REMOTE CHECK RESPONSE:"
1154
+ # puts JSON.pretty_generate(check_json_response), "\n"
1155
+ app_map[:last_check][:http_status] = 200
1156
+ app_map[:build_version] = check_json_response['buildVersion'] # || check_json_response['build_version']
1157
+ #app_map[:last_check][:success] = true
1158
+ if check_json_response['success'] == true
1159
+ app_map[:status] = 'ready'
1160
+ app_map[:last_check][:success] = true
1161
+ # consider bumping this after every successful api command
1162
+ app_map[:last_success_at] = Time.now.to_i
1163
+ app_map.delete(:error)
1164
+ end
1165
+ if check_json_response['setupNeeded'] == true
1166
+ app_map[:setup_needed] = true
1167
+ app_map[:status] = 'fresh'
1168
+ else
1169
+ app_map.delete(:setup_needed)
1170
+ end
1171
+
1172
+ rescue SocketError => err
1173
+ app_map[:status] = 'unreachable'
1174
+ app_map[:last_check][:http_status] = nil
1175
+ app_map[:last_check][:error] = err.message
1176
+ rescue RestClient::Exceptions::Timeout => err
1177
+ # print_rest_exception(e, options)
1178
+ # exit 1
1179
+ app_map[:status] = 'http-timeout'
1180
+ app_map[:last_check][:http_status] = nil
1181
+ rescue Errno::ECONNREFUSED => err
1182
+ app_map[:status] = 'net-error'
1183
+ app_map[:last_check][:error] = err.message
1184
+ rescue OpenSSL::SSL::SSLError => err
1185
+ app_map[:status] = 'ssl-error'
1186
+ app_map[:last_check][:error] = err.message
1187
+ rescue RestClient::Exception => err
1188
+ app_map[:status] = 'http-error'
1189
+ app_map[:http_status] = err.response ? err.response.code : nil
1190
+ app_map[:last_check][:error] = err.message
1191
+ # fallback to /ping for older appliance versions (pre 2.10.5)
1192
+ begin
1193
+ Morpheus::Logging::DarkPrinter.puts "falling back to remote check via /ping ..." if Morpheus::Logging.debug?
1194
+ setup_interface.ping()
1195
+ app_map[:last_check][:ping_fallback] = true
1196
+ app_map[:last_check][:http_status] = 200
1197
+ app_map[:last_check][:success] = true
1198
+ app_map[:last_check][:ping_fallback] = true
1199
+ app_map[:build_version] = "" # unknown until whoami is executed..
1200
+ app_map[:status] = 'ready'
1201
+ # consider bumping this after every successful api command
1202
+ app_map[:last_success_at] = Time.now.to_i
1203
+ app_map.delete(:error)
1204
+ rescue => ping_err
1205
+ Morpheus::Logging::DarkPrinter.puts "/ping failed too: #{ping_err.message} ..." if Morpheus::Logging.debug?
1206
+ end
1207
+ rescue => err
1208
+ # should save before raising atleast..sheesh
1209
+ raise err
1210
+ # Morpheus::Cli::ErrorHandler.new.handle_error(e)
1211
+ app_map[:status] = 'error'
1212
+ app_map[:last_check][:error] = err.message
1213
+ end
1214
+
1215
+ # if app_map[:status] == 'ready'
1216
+ # app_map.delete(:error)
1217
+ # end
1218
+
1219
+ # save changes to disk ... and
1220
+ # ... class variable returned by Remote.appliances is updated in there too...
1221
+ save_remote(app_name, app_map)
1222
+
1223
+ # return the updated data
1224
+ return app_map
1225
+
1226
+ end
1227
+
488
1228
  end
489
1229
 
490
1230
  end