morpheus-cli 2.10.3 → 2.11.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -60,9 +60,14 @@ class Morpheus::Cli::Roles
60
60
  print JSON.pretty_generate(json_response)
61
61
  print "\n"
62
62
  else
63
- print "\n" ,cyan, bold, "Morpheus Roles\n","==================", reset, "\n\n"
63
+ title = "Morpheus Roles"
64
+ subtitles = []
65
+ if params[:phrase]
66
+ subtitles << "Search: #{params[:phrase]}".strip
67
+ end
68
+ print_h1 title, subtitles
64
69
  if roles.empty?
65
- puts yellow,"No roles found.",reset
70
+ print cyan,"No roles found.",reset,"\n"
66
71
  else
67
72
  print_roles_table(roles, {is_master_account: @is_master_account})
68
73
  print_results_pagination(json_response)
@@ -105,46 +110,68 @@ class Morpheus::Cli::Roles
105
110
  puts optparse
106
111
  exit 1
107
112
  end
108
- name = args[0]
109
113
 
110
114
  connect(options)
111
115
  begin
112
116
  account = find_account_from_options(options)
113
117
  account_id = account ? account['id'] : nil
114
118
  if options[:dry_run]
115
- print_dry_run @roles_interface.dry.list(account_id, {name: name})
119
+ if args[0].to_s =~ /\A\d{1,}\Z/
120
+ print_dry_run @roles_interface.dry.get(account_id, args[0].to_i)
121
+ else
122
+ print_dry_run @roles_interface.dry.list(account_id, {name: args[0]})
123
+ end
116
124
  return
117
125
  end
118
126
 
119
- role = find_role_by_name_or_id(account_id, name)
120
- exit 1 if role.nil?
127
+ # role = find_role_by_name_or_id(account_id, args[0])
128
+ # exit 1 if role.nil?
129
+ # refetch from show action, argh
130
+ # json_response = @roles_interface.get(account_id, role['id'])
131
+ # role = json_response['role']
121
132
 
122
- json_response = @roles_interface.get(account_id, role['id'])
123
- role = json_response['role']
133
+ json_response = nil
134
+ if args[0].to_s =~ /\A\d{1,}\Z/
135
+ json_response = @roles_interface.get(account_id, args[0].to_i)
136
+ role = json_response['role']
137
+ else
138
+ role = find_role_by_name_or_id(account_id, args[0])
139
+ exit 1 if role.nil?
140
+ # refetch from show action, argh
141
+ json_response = @roles_interface.get(account_id, role['id'])
142
+ role = json_response['role']
143
+ end
124
144
 
125
145
  if options[:json]
126
146
  print JSON.pretty_generate(json_response)
127
147
  print "\n"
128
148
  else
129
- print "\n" ,cyan, bold, "Role Details\n","==================", reset, "\n\n"
130
149
  print cyan
131
- puts "ID: #{role['id']}"
132
- puts "Name: #{role['authority']}"
133
- puts "Description: #{role['description']}"
134
- puts "Scope: #{role['scope']}"
135
- puts "Type: #{format_role_type(role)}"
136
- puts "Multitenant: #{role['multitenant'] ? 'Yes' : 'No'}"
137
- puts "Owner: #{role['owner'] ? role['owner']['name'] : nil}"
138
- puts "Date Created: #{format_local_dt(role['dateCreated'])}"
139
- puts "Last Updated: #{format_local_dt(role['lastUpdated'])}"
140
-
141
- print "\n" ,cyan, bold, "Role Instance Limits\n","==================", reset, "\n\n"
150
+ print_h1 "Role Details"
151
+ print cyan
152
+ description_cols = {
153
+ "ID" => 'id',
154
+ "Name" => 'name',
155
+ "Description" => 'description',
156
+ "Scope" => lambda {|it| it['scope'] },
157
+ "Type" => lambda {|it| format_role_type(it) },
158
+ "Multitenant" => lambda {|it| format_boolean(it['multitenant']) },
159
+ "Owner" => lambda {|it| role['owner'] ? role['owner']['name'] : '' },
160
+ #"Account" => lambda {|it| it['account'] ? it['account']['name'] : '' },
161
+ "Created" => lambda {|it| format_local_dt(it['dateCreated']) },
162
+ "Updated" => lambda {|it| format_local_dt(it['lastUpdated']) }
163
+ }
164
+ print_description_list(description_cols, role)
165
+
166
+ print_h2 "Role Instance Limits"
142
167
  print cyan
143
- puts "Max Storage (bytes): #{role['instanceLimits'] ? role['instanceLimits']['maxStorage'] : 0}"
144
- puts "Max Memory (bytes): #{role['instanceLimits'] ? role['instanceLimits']['maxMemory'] : 0}"
145
- puts "CPU Count: #{role['instanceLimits'] ? role['instanceLimits']['maxCpu'] : 0}"
168
+ print_description_list({
169
+ "Max Storage" => lambda {|it| (it && it['maxStorage'].to_i != 0) ? Filesize.from("#{it['maxStorage']} B").pretty : "no limit" },
170
+ "Max Memory" => lambda {|it| (it && it['maxMemory'].to_i != 0) ? Filesize.from("#{it['maxMemory']} B").pretty : "no limit" },
171
+ "CPU Count" => lambda {|it| (it && it['maxCpu'].to_i != 0) ? it['maxCpu'] : "no limit" }
172
+ }, role['instanceLimits'])
146
173
 
147
- print "\n" ,cyan, bold, "Feature Access\n","==================", reset, "\n\n"
174
+ print_h2 "Feature Access"
148
175
  print cyan
149
176
 
150
177
  if options[:include_feature_access]
@@ -160,7 +187,7 @@ class Morpheus::Cli::Roles
160
187
  puts "Use --feature-access to list feature access"
161
188
  end
162
189
 
163
- print "\n" ,cyan, bold, "Group Access\n","==================", reset, "\n\n"
190
+ print_h2 "Group Access"
164
191
  print cyan
165
192
  puts "Global Group Access: #{get_access_string(json_response['globalSiteAccess'])}\n\n"
166
193
  if json_response['globalSiteAccess'] == 'custom'
@@ -177,7 +204,7 @@ class Morpheus::Cli::Roles
177
204
  end
178
205
  end
179
206
 
180
- print "\n" ,cyan, bold, "Cloud Access\n","==================", reset, "\n\n"
207
+ print_h2 "Cloud Access"
181
208
  print cyan
182
209
  puts "Global Cloud Access: #{get_access_string(json_response['globalZoneAccess'])}\n\n"
183
210
  if json_response['globalZoneAccess'] == 'custom'
@@ -194,7 +221,7 @@ class Morpheus::Cli::Roles
194
221
  end
195
222
  end
196
223
 
197
- print "\n" ,cyan, bold, "Instance Type Access\n","==================", reset, "\n\n"
224
+ print_h2 "Instance Type Access"
198
225
  print cyan
199
226
  puts "Global Instance Type Access: #{get_access_string(json_response['globalInstanceTypeAccess'])}\n\n"
200
227
  if json_response['globalInstanceTypeAccess'] == 'custom'
@@ -167,9 +167,9 @@ EOT
167
167
  return
168
168
  end
169
169
  rules = json_response['rules']
170
- print "\n" ,cyan, bold, "Morpheus Security Group Rules for Security Group ID:#{security_group_id}\n","==================", reset, "\n\n"
170
+ print_h1 "Morpheus Security Group Rules for Security Group ID: #{security_group_id}"
171
171
  if rules.empty?
172
- puts yellow,"No Security Group Rules currently configured.",reset
172
+ print yellow,"No Security Group Rules currently configured.",reset,"\n"
173
173
  else
174
174
  rules = rules.sort {|x,y| x["id"] <=> y["id"] }
175
175
  rules.each do |rule|
@@ -44,9 +44,9 @@ class Morpheus::Cli::SecurityGroups
44
44
  return
45
45
  end
46
46
  security_groups = json_response['securityGroups']
47
- print "\n" ,cyan, bold, "Morpheus Security Groups\n","==================", reset, "\n\n"
47
+ print_h1 "Morpheus Security Groups"
48
48
  if security_groups.empty?
49
- puts yellow,"No Security Groups currently configured.",reset
49
+ print yellow,"No Security Groups currently configured.",reset,"\n"
50
50
  else
51
51
  active_id = @active_security_group[@appliance_name.to_sym]
52
52
  security_groups.each do |security_group|
@@ -57,7 +57,7 @@ class Morpheus::Cli::SecurityGroups
57
57
  end
58
58
  end
59
59
  if active_id
60
- print cyan, "\n\n# => - current", reset
60
+ print cyan, "\n# => - current", reset, "\n"
61
61
  end
62
62
  end
63
63
  print reset,"\n"
@@ -91,14 +91,17 @@ class Morpheus::Cli::SecurityGroups
91
91
  return
92
92
  end
93
93
  security_group = json_response['securityGroup']
94
- print "\n" ,cyan, bold, "Morpheus Security Group\n","==================", reset, "\n\n"
95
- if security_group.nil?
96
- puts yellow,"Security Group not found by id #{args[0]}",reset
97
- else
98
- print cyan, "= #{security_group['id']}: #{security_group['name']} (#{security_group['description']})\n"
99
- end
100
- print reset,"\n\n"
101
- rescue RestClient::Exception => e
94
+ print_h1 "Morpheus Security Group"
95
+ print cyan
96
+ description_cols = {
97
+ "ID" => 'id',
98
+ "Name" => 'name',
99
+ "Description" => 'description',
100
+ #"Account" => lambda {|it| it['account'] ? it['account']['name'] : '' },
101
+ }
102
+ print_description_list(description_cols, security_group)
103
+ print reset,"\n"
104
+ rescue RestClient::Exception => e
102
105
  print_rest_exception(e, options)
103
106
  exit 1
104
107
  end
@@ -235,16 +238,17 @@ class Morpheus::Cli::SecurityGroups
235
238
  end
236
239
 
237
240
  def self.security_group_file_path
238
- home_dir = Dir.home
239
- morpheus_dir = File.join(home_dir,".morpheus")
240
- if !Dir.exist?(morpheus_dir)
241
- Dir.mkdir(morpheus_dir)
242
- end
243
- return File.join(morpheus_dir,"securitygroup")
241
+ File.join(Morpheus::Cli.home_directory,"securitygroup")
244
242
  end
245
243
 
246
- def self.save_security_group(security_group_map)
247
- File.open(security_group_file_path, 'w') {|f| f.write security_group_map.to_yaml } #Store
244
+ def self.save_security_group(new_config)
245
+ fn = security_group_file_path
246
+ if !Dir.exists?(File.dirname(fn))
247
+ FileUtils.mkdir_p(File.dirname(fn))
248
+ end
249
+ File.open(fn, 'w') {|f| f.write new_config.to_yaml } #Store
250
+ FileUtils.chmod(0600, fn)
251
+ new_config
248
252
  end
249
253
 
250
254
  end
@@ -0,0 +1,50 @@
1
+ require 'optparse'
2
+ require 'json'
3
+ require 'morpheus/logging'
4
+ require 'morpheus/cli/cli_command'
5
+
6
+ class Morpheus::Cli::SetPromptCommand
7
+ include Morpheus::Cli::CliCommand
8
+ set_command_name :'set-prompt'
9
+ set_command_hidden
10
+
11
+ def handle(args)
12
+ options = {}
13
+ optparse = Morpheus::Cli::OptionParser.new do|opts|
14
+ opts.banner = "Usage: morpheus #{command_name} [shell-prompt]"
15
+ #build_common_options(opts, options, [])
16
+ opts.on('-h', '--help', "Prints this help" ) do
17
+ puts opts
18
+ exit
19
+ end
20
+ opts.footer = <<-EOT
21
+ This is intended for use in your morpheus scripts.
22
+ It allows you to set the shell prompt.
23
+ This can be used as alternative to setting the MORPHEUS_PS1 environment variable
24
+
25
+ Examples:
26
+ set-prompt "morpheus $ "
27
+ set-prompt "%cyanmorpheus> "
28
+ set-prompt "[%magenta%remote%reset] %cyan%username morpheus> "
29
+
30
+ The default prompt is: "%cyanmorpheus> "
31
+
32
+ EOT
33
+ end
34
+ optparse.parse!(args)
35
+
36
+ if args.count != 1
37
+ print_error Morpheus::Terminal.angry_prompt
38
+ puts_error "too many arguments"
39
+ puts_error optparse
40
+ return false
41
+ end
42
+
43
+ self.my_terminal.prompt = args[0]
44
+ # Morpheus::Terminal.instance.prompt = args[0]
45
+ Morpheus::Cli::Shell.instance.recalculate_prompt()
46
+
47
+ return true
48
+ end
49
+
50
+ end
@@ -8,22 +8,43 @@ require 'logger'
8
8
  require 'fileutils'
9
9
  require 'morpheus/cli/cli_command'
10
10
  require 'morpheus/cli/error_handler'
11
+ require 'morpheus/terminal'
11
12
 
12
-
13
+ #class Morpheus::Cli::Shell < Morpheus::Terminal
13
14
  class Morpheus::Cli::Shell
14
15
  include Morpheus::Cli::CliCommand
15
16
 
16
17
  @@instance = nil
17
18
 
18
19
  def self.instance
19
- @@instance ||= self.new
20
+ @@instance ||= reload_instance
21
+ end
22
+
23
+ def self.reload_instance
24
+ @@instance = self.new
20
25
  end
21
26
 
27
+ attr_accessor :prompt #, :angry_prompt
28
+
22
29
  def initialize()
23
- @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
30
+ @@instance = self
31
+ reinitialize()
32
+ end
33
+
34
+ def reinitialize()
35
+ # @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
36
+ @current_remote = ::Morpheus::Cli::Remote.load_active_remote()
37
+ if @current_remote
38
+ @appliance_name, @appliance_url = @current_remote[:name], @current_remote[:host]
39
+ @current_username = @current_remote[:username] || '(anonymous)'
40
+ else
41
+ @appliance_name, @appliance_url = nil, nil
42
+ @current_username = nil
43
+ end
24
44
  #connect()
25
45
  #raise "one shell only" if @@instance
26
46
  @@instance = self
47
+ recalculate_prompt()
27
48
  recalculate_auto_complete_commands()
28
49
  end
29
50
 
@@ -31,6 +52,29 @@ class Morpheus::Cli::Shell
31
52
  # @api_client = establish_remote_appliance_connection(opts)
32
53
  # end
33
54
 
55
+ def recalculate_prompt()
56
+ # custom prompts.. this is overkill and perhaps a silly thing..
57
+ # Example usage:
58
+ # MORPHEUS_PS1="[%remote] %cyan %username morph> " morpheus shell --debug
59
+ #@prompt = Morpheus::Terminal.instance.prompt.to_s #.dup
60
+ @prompt = my_terminal.prompt
61
+
62
+ var_map = {
63
+ '%cyan' => cyan, '%magenta' => magenta, '%reset' => reset, '%dark' => dark,
64
+ '%remote' => @appliance_name.to_s, '%username' => @current_username.to_s,
65
+ '%remote_url' => @appliance_url.to_s
66
+ }
67
+ @calculated_prompt = @prompt.to_s.dup
68
+ var_map.each do |var_key, var_value|
69
+ @calculated_prompt.gsub!(var_key.to_s, var_value.to_s)
70
+ end
71
+ # cleanup empty brackets caused by var value
72
+ @calculated_prompt = @calculated_prompt.gsub("[]", "").gsub("<>", "").gsub("{}", "")
73
+ @calculated_prompt = @calculated_prompt.strip
74
+ @calculated_prompt = "#{@calculated_prompt}#{reset} "
75
+ @calculated_prompt
76
+ end
77
+
34
78
  def recalculate_auto_complete_commands
35
79
  @morpheus_commands = Morpheus::Cli::CliRegistry.all.keys.reject {|k| [:shell].include?(k) }
36
80
  @shell_commands = [:clear, :history, :'flush-history', :reload!, :help, :exit]
@@ -57,7 +101,6 @@ class Morpheus::Cli::Shell
57
101
 
58
102
  def handle(args)
59
103
  usage = "Usage: morpheus #{command_name}"
60
- @command_options = {} # this is a way to curry options to all commands.. but meh
61
104
  optparse = OptionParser.new do|opts|
62
105
  opts.banner = usage
63
106
  opts.on('--norc','--norc', "Do not read and execute the personal initialization script .morpheusrc") do
@@ -67,13 +110,11 @@ class Morpheus::Cli::Shell
67
110
  Morpheus::RestClient.enable_ssl_verification = false
68
111
  end
69
112
  opts.on('-C','--nocolor', "Disable ANSI coloring") do
70
- @command_options[:nocolor] = true
71
113
  Term::ANSIColor::coloring = false
72
114
  end
73
115
  opts.on('-V','--debug', "Print extra output for debugging. ") do |json|
74
116
  Morpheus::Logging.set_log_level(Morpheus::Logging::Logger::DEBUG)
75
- ::RestClient.log = Morpheus::Logging.debug? ? STDOUT : nil
76
- @command_options[:debug] = true
117
+ ::RestClient.log = Morpheus::Logging.debug? ? Morpheus::Logging::DarkPrinter.instance : nil
77
118
  end
78
119
  opts.on( '-h', '--help', "Prints this help" ) do
79
120
  puts opts
@@ -95,13 +136,17 @@ class Morpheus::Cli::Shell
95
136
  end
96
137
  end
97
138
 
139
+ reinitialize()
140
+ # recalculate_prompt()
141
+ # recalculate_auto_complete_commands()
142
+
98
143
  exit = false
99
144
  while !exit do
100
145
  Readline.completion_append_character = " "
101
146
  Readline.completion_proc = @auto_complete
102
147
  Readline.basic_word_break_characters = ""
103
148
  #Readline.basic_word_break_characters = "\t\n\"\‘`@$><=;|&{( "
104
- input = Readline.readline("#{cyan}morpheus> #{reset}", true).to_s
149
+ input = Readline.readline(@calculated_prompt, true).to_s
105
150
  input = input.strip
106
151
 
107
152
  execute_commands(input)
@@ -109,7 +154,15 @@ class Morpheus::Cli::Shell
109
154
 
110
155
  end
111
156
 
157
+ # same as Terminal instance
158
+ def execute(input)
159
+ # args = Shellwords.shellsplit(input)
160
+ #cmd = args.shift
161
+ execute_commands(input)
162
+ end
163
+
112
164
  def execute_commands(input)
165
+ # input = input.to_s.sub(/^morpheus\s+/, "") # meh
113
166
  # split the command on unquoted semicolons.
114
167
  # so you can run multiple commands at once! eg hosts list; instances list
115
168
  # all_commands = input.gsub(/(\;)(?=(?:[^"]|"[^"]*")*$)/, '__CMDDELIM__').split('__CMDDELIM__').collect {|it| it.to_s.strip }.select {|it| !it.empty? }.compact
@@ -126,12 +179,8 @@ class Morpheus::Cli::Shell
126
179
 
127
180
  def execute_command(input)
128
181
  #puts "shell execute_command(#{input})"
129
- @command_options ||= {}
130
-
131
182
  input = input.to_s.strip
132
183
 
133
- # print cyan,"morpheus > ",reset
134
- # input = $stdin.gets.chomp!
135
184
  if !input.empty?
136
185
 
137
186
  if input == 'exit'
@@ -140,6 +189,9 @@ class Morpheus::Cli::Shell
140
189
  exit 0
141
190
  elsif input == 'help'
142
191
 
192
+ #print_h1 "Morpheus Shell Help", [], white
193
+ #print "\n"
194
+
143
195
  puts "You are in a morpheus client shell."
144
196
  puts "See the available commands below."
145
197
 
@@ -149,15 +201,29 @@ class Morpheus::Cli::Shell
149
201
  @morpheus_commands.sort.each {|cmd|
150
202
  puts "\t#{cmd.to_s}"
151
203
  }
152
- puts "\n"
204
+ #puts "\n"
153
205
  puts "\nShell Commands:"
154
206
  @shell_commands.each {|cmd|
155
207
  puts "\t#{cmd.to_s}"
156
208
  }
157
209
  puts "\n"
158
- puts "For more information."
159
- puts "See https://github.com/gomorpheus/morpheus-cli/wiki"
160
- print "\n"
210
+ puts "For more information, see https://github.com/gomorpheus/morpheus-cli/wiki"
211
+ #print "\n"
212
+ return 0
213
+ elsif input =~ /^sleep/
214
+ sleep_sec = input.sub("sleep ", "").to_f
215
+ if (!(sleep_sec > 0))
216
+ # raise_command_error "sleep requires the argument [seconds]. eg. sleep 3.14"
217
+ puts_error "sleep requires argument [seconds]. eg. sleep 3.14"
218
+ return false
219
+ end
220
+ log_history_command(input)
221
+ Morpheus::Logging::DarkPrinter.puts "sleeping for #{sleep_sec}s ... zzzZzzzZ" if Morpheus::Logging.debug?
222
+ begin
223
+ sleep(sleep_sec)
224
+ rescue Interrupt
225
+ Morpheus::Logging::DarkPrinter.puts "\nInterrupt. waking up from sleep early"
226
+ end
161
227
  return 0
162
228
  elsif input =~ /^history/
163
229
  n_commands = input.sub(/^history\s?/, '').sub(/\-n\s?/, '')
@@ -182,32 +248,74 @@ class Morpheus::Cli::Shell
182
248
  @history_logger = load_history_logger
183
249
  puts "history cleared!"
184
250
  return 0
251
+ elsif input == "edit rc"
252
+ fn = Morpheus::Cli::DotFile.morpheusrc_filename
253
+ editor = ENV['EDITOR'] # || 'nano'
254
+ if !editor
255
+ puts "You have no EDITOR defined. Use 'export EDITOR=emacs'"
256
+ #puts "Trying nano..."
257
+ #editor = "nano"
258
+ end
259
+ system("which #{editor} > /dev/null 2>&1")
260
+ has_editor = $?.success?
261
+ if has_editor
262
+ puts "opening #{fn} for editing with #{editor} ..."
263
+ system("#{editor} #{fn}")
264
+ puts "Use 'reload' to re-execute your startup script #{File.basename(fn)}"
265
+ else
266
+ puts_error2 Morpheus::Terminal.angry_prompt
267
+ puts_error "The defined EDITOR '#{editor}' was not found on your system."
268
+ end
269
+ return 0 # $?
270
+ elsif input == "edit profile"
271
+ fn = Morpheus::Cli::DotFile.morpheus_profile_filename
272
+ editor = ENV['EDITOR'] # || 'nano'
273
+ if !editor
274
+ puts "You have no EDITOR defined. Use 'export EDITOR=emacs'."
275
+ #puts "Trying nano..."
276
+ #editor = "nano"
277
+ end
278
+ system("which #{editor} > /dev/null 2>&1")
279
+ has_editor = $?.success?
280
+ if has_editor
281
+ puts "opening #{fn} for editing with #{editor} ..."
282
+ `#{editor} #{fn}`
283
+ puts "Use 'reload' to re-execute your startup script #{File.basename(fn)}"
284
+ else
285
+ puts_error Morpheus::Terminal.angry_prompt
286
+ puts_error "The defined EDITOR '#{editor}' was not found on your system."
287
+ end
288
+ return 0 # $?
185
289
  elsif input == 'reload' || input == 'reload!'
290
+ # raise RestartShellPlease
186
291
  #log_history_command(input)
187
292
  # could just fork instead?
293
+ # clear registry
294
+ Morpheus::Cli::CliRegistry.instance.flush
295
+ # reload code
188
296
  Morpheus::Cli.load!
189
- # initialize()
190
- # gotta reload appliance, groups, credentials
191
- @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
192
- recalculate_auto_complete_commands()
193
- # execute startup script
194
- # if File.exists?(Morpheus::Cli::DotFile.morpheusrc_filename)
195
- # Morpheus::Cli::DotFile.new(Morpheus::Cli::DotFile.morpheusrc_filename).execute()
196
- # end
197
- begin
198
- load __FILE__
199
- rescue => err
200
- print "failed to reload #{__FILE__}. oh well"
201
- # print err
297
+
298
+ # raise RestartShellPlease
299
+
300
+ # execute startup scripts
301
+ if File.exists?(Morpheus::Cli::DotFile.morpheus_profile_filename)
302
+ Morpheus::Cli::DotFile.new(Morpheus::Cli::DotFile.morpheus_profile_filename).execute()
303
+ end
304
+ if File.exists?(Morpheus::Cli::DotFile.morpheusrc_filename)
305
+ Morpheus::Cli::DotFile.new(Morpheus::Cli::DotFile.morpheusrc_filename).execute()
202
306
  end
203
- print dark," #=> shell has been reloaded",reset,"\n" if Morpheus::Logging.debug?
307
+
308
+ # recalculate shell environment
309
+ reinitialize()
310
+
311
+ Morpheus::Logging::DarkPrinter.puts "shell has been reloaded" if Morpheus::Logging.debug?
204
312
  return 0
205
313
  elsif input == '!!'
206
314
  cmd_number = @history.keys[-1]
207
315
  input = @history[cmd_number]
208
316
  if !input
209
317
  puts "There is no previous command"
210
- return 1
318
+ return false
211
319
  end
212
320
  execute_commands(input)
213
321
  elsif input =~ /^\!.+/
@@ -237,76 +345,29 @@ class Morpheus::Cli::Shell
237
345
  # puts "#{Morpheus::Logging.log_level}"
238
346
  elsif input == "debug"
239
347
  log_history_command(input)
240
- Morpheus::Cli::LogLevelCommand.new.handle(["debug"])
241
- @command_options[:debug] = true
242
- return 0
243
- # Morpheus::Logging.set_log_level(Morpheus::Logging::Logger::DEBUG)
244
- # ::RestClient.log = Morpheus::Logging.debug? ? STDOUT : nil
245
- # elsif log_level == "info"
246
- # #log_history_command(input)
247
- # @command_options.delete(:debug)
248
- # Morpheus::Logging.set_log_level(Morpheus::Logging::Logger::INFO)
249
- # ::RestClient.log = Morpheus::Logging.debug? ? STDOUT : nil
250
- # elsif log_level.to_s == "0" || (log_level.to_i > 0 && log_level.to_i < 7)
251
- # # other log levels are pointless right now..
252
- # @command_options.delete(:debug)
253
- # Morpheus::Logging.set_log_level(log_level.to_i)
254
- # ::RestClient.log = Morpheus::Logging.debug? ? STDOUT : nil
255
- # else
256
- # print_red_alert "unknown log level: #{log_level}"
257
- # end
258
- # return 0
259
- # # lots of hidden commands
260
- # elsif input == "debug"
261
- # @command_options[:debug] = true
262
- # Morpheus::Logging.set_log_level(Morpheus::Logging::Logger::DEBUG)
263
- # ::RestClient.log = Morpheus::Logging.debug? ? STDOUT : nil
264
- # return 0
348
+ return Morpheus::Cli::LogLevelCommand.new.handle(["debug"])
265
349
  elsif ["hello","hi","hey","hola"].include?(input.strip.downcase)
266
350
  print "#{input.capitalize}, how may I #{cyan}help#{reset} you?\n"
267
351
  return 0
268
- # use morpheus coloring [on|off]
269
- # elsif input == "colorize"
270
- # Term::ANSIColor::coloring = true
271
- # @command_options[:nocolor] = false
272
- # return 0
273
- # elsif input == "uncolorize"
274
- # Term::ANSIColor::coloring = false
275
- # @command_options[:nocolor] = true
276
- # return 0
277
352
  elsif input == "shell"
278
353
  print "#{cyan}You are already in a shell.#{reset}\n"
279
354
  return false
280
- # there is actually a Cli::SourceCommand !
281
- # elsif input =~ /^source\s*/i
282
- # source_file = input.sub(/^source\s*/i, '')
283
- # # execute a source script
284
- # if File.exists?(source_file)
285
- # cmd_results = Morpheus::Cli::DotFile.new(source_file).execute()
286
- # # return !cmd_results.include?(false)
287
- # return true
288
- # else
289
- # print_red_alert "file not found: '#{source_file}'"
290
- # return false
291
- # end
292
- # there is actually a Cli::EchoCommand !
293
- # elsif input =~ /^echo\s*/i
294
- # your_words = input.sub(/^echo\s*/i, '')
295
- # print your_words.to_s + "\n"
296
- # return true
355
+ elsif input =~ /^\.\s/
356
+ # dot alias for source <file>
357
+ log_history_command(input)
358
+ return Morpheus::Cli::SourceCommand.new.handle(input.split[1..-1])
297
359
  end
298
360
 
299
361
  begin
300
362
  argv = Shellwords.shellsplit(input)
301
-
302
-
303
- if Morpheus::Cli::CliRegistry.has_command?(argv[0]) || Morpheus::Cli::CliRegistry.has_alias?(argv[0])
363
+ cmd_name = argv[0]
364
+ cmd_args = argv[1..-1]
365
+ if Morpheus::Cli::CliRegistry.has_command?(cmd_name) || Morpheus::Cli::CliRegistry.has_alias?(cmd_name)
304
366
  #log_history_command(input)
305
- Morpheus::Cli::CliRegistry.exec(argv[0], argv[1..-1])
367
+ Morpheus::Cli::CliRegistry.exec(cmd_name, cmd_args)
306
368
  else
307
- print_yellow_warning "Unrecognized Command '#{argv[0]}'. Try 'help' to see a list of available commands."
308
- @history_logger.warn "Unrecognized Command #{argv[0]}" if @history_logger
309
- #puts optparse
369
+ puts_error "#{Morpheus::Terminal.angry_prompt}'#{cmd_name}' is not a morpheus command. Use 'help' to see the list of available commands."
370
+ @history_logger.warn "Unrecognized Command #{cmd_name}" if @history_logger
310
371
  end
311
372
  # rescue ArgumentError
312
373
  # puts "Argument Syntax Error..."
@@ -319,13 +380,13 @@ class Morpheus::Cli::Shell
319
380
  # print "\n"
320
381
  rescue => e
321
382
  @history_logger.error "#{e.message}" if @history_logger
322
- Morpheus::Cli::ErrorHandler.new.handle_error(e, @command_options)
383
+ Morpheus::Cli::ErrorHandler.new(my_terminal.stderr).handle_error(e) # lol
323
384
  # exit 1
324
385
  end
325
386
 
326
387
  if @return_to_log_level
327
388
  Morpheus::Logging.set_log_level(@return_to_log_level)
328
- ::RestClient.log = Morpheus::Logging.debug? ? STDOUT : nil
389
+ ::RestClient.log = Morpheus::Logging.debug? ? Morpheus::Logging::DarkPrinter.instance : nil
329
390
  @return_to_log_level = nil
330
391
  end
331
392
 
@@ -334,84 +395,88 @@ class Morpheus::Cli::Shell
334
395
  end
335
396
 
336
397
  def get_prompt
398
+
399
+ # print cyan,"morpheus > ",reset
400
+ # input = $stdin.gets.chomp!
401
+
337
402
  input = ''
338
403
  while char=$stdin.getch do
339
- if char == '\n'
340
- print "\r\n"
341
- puts "executing..."
342
- break
343
- end
344
- print char
345
- input << char
404
+ if char == '\n'
405
+ print "\r\n"
406
+ puts "executing..."
407
+ break
346
408
  end
347
- return input
409
+ print char
410
+ input << char
348
411
  end
412
+ return input
413
+ end
349
414
 
350
- def history_file_path
351
- File.join(Morpheus::Cli.home_directory, "shell_history")
352
- end
415
+ def history_file_path
416
+ File.join(Morpheus::Cli.home_directory, "shell_history")
417
+ end
353
418
 
354
- def load_history_logger
355
- file_path = history_file_path
356
- if !Dir.exists?(File.dirname(file_path))
357
- FileUtils.mkdir_p(File.dirname(file_path))
358
- end
359
- if !File.exists?(file_path)
360
- FileUtils.touch(file_path)
361
- FileUtils.chmod(0600, file_path)
362
- end
363
- logger = Logger.new(file_path)
364
- # logger.formatter = proc do |severity, datetime, progname, msg|
365
- # "#{msg}\n"
366
- # end
367
- return logger
419
+ def load_history_logger
420
+ file_path = history_file_path
421
+ if !Dir.exists?(File.dirname(file_path))
422
+ FileUtils.mkdir_p(File.dirname(file_path))
368
423
  end
424
+ if !File.exists?(file_path)
425
+ FileUtils.touch(file_path)
426
+ FileUtils.chmod(0600, file_path)
427
+ end
428
+ logger = Logger.new(file_path)
429
+ # logger.formatter = proc do |severity, datetime, progname, msg|
430
+ # "#{msg}\n"
431
+ # end
432
+ return logger
433
+ end
369
434
 
370
- def load_history_from_log_file(n_commands=1000)
371
- @history ||= {}
372
- @last_command_number ||= 0
435
+ def load_history_from_log_file(n_commands=1000)
436
+ @history ||= {}
437
+ @last_command_number ||= 0
373
438
 
374
- begin
375
- if Gem.win_platform?
376
- return @history
377
- end
378
- file_path = history_file_path
379
- FileUtils.mkdir_p(File.dirname(file_path))
380
- # grab extra lines because not all log entries are commands
381
- n_lines = n_commands + 500
382
- history_lines = `tail -n #{n_lines} #{file_path}`.split(/\n/)
383
- command_lines = history_lines.select do |line|
384
- line.match(/\(cmd (\d+)\) (.+)/)
385
- end
386
- command_lines = command_lines.last(n_commands)
387
- command_lines.each do |line|
388
- matches = line.match(/\(cmd (\d+)\) (.+)/)
389
- if matches && matches.size == 3
390
- cmd_number = matches[1].to_i
391
- cmd = matches[2]
392
-
393
- @last_command_number = cmd_number
394
- @history[@last_command_number] = cmd
395
-
396
- # for Ctrl+R history searching
397
- Readline::HISTORY << cmd
398
- end
439
+ begin
440
+ if Gem.win_platform?
441
+ return @history
442
+ end
443
+ file_path = history_file_path
444
+ FileUtils.mkdir_p(File.dirname(file_path))
445
+ # grab extra lines because not all log entries are commands
446
+ n_lines = n_commands + 500
447
+ history_lines = `tail -n #{n_lines} #{file_path}`.split(/\n/)
448
+ command_lines = history_lines.select do |line|
449
+ line.match(/\(cmd (\d+)\) (.+)/)
450
+ end
451
+ command_lines = command_lines.last(n_commands)
452
+ command_lines.each do |line|
453
+ matches = line.match(/\(cmd (\d+)\) (.+)/)
454
+ if matches && matches.size == 3
455
+ cmd_number = matches[1].to_i
456
+ cmd = matches[2]
457
+
458
+ @last_command_number = cmd_number
459
+ @history[@last_command_number] = cmd
460
+
461
+ # for Ctrl+R history searching
462
+ Readline::HISTORY << cmd
399
463
  end
400
- rescue => e
401
- # raise e
402
- # puts "failed to load history from log"
403
- @history = {}
404
464
  end
405
- return @history
465
+ rescue => e
466
+ # raise e
467
+ # puts "failed to load history from log"
468
+ @history = {}
406
469
  end
470
+ return @history
471
+ end
407
472
 
408
- def log_history_command(cmd)
409
- @history ||= {}
410
- @last_command_number ||= 0
411
- @last_command_number += 1
412
- @history[@last_command_number] = cmd
413
- if @history_logger
414
- @history_logger.info "(cmd #{@last_command_number}) #{cmd}"
415
- end
473
+ def log_history_command(cmd)
474
+ @history ||= {}
475
+ @last_command_number ||= 0
476
+ @last_command_number += 1
477
+ @history[@last_command_number] = cmd
478
+ if @history_logger
479
+ @history_logger.info "(cmd #{@last_command_number}) #{cmd}"
416
480
  end
417
481
  end
482
+ end