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
@@ -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