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.
- checksums.yaml +4 -4
- data/bin/morpheus +5 -96
- data/lib/morpheus/api/api_client.rb +23 -1
- data/lib/morpheus/api/checks_interface.rb +106 -0
- data/lib/morpheus/api/incidents_interface.rb +102 -0
- data/lib/morpheus/api/monitoring_apps_interface.rb +47 -0
- data/lib/morpheus/api/monitoring_contacts_interface.rb +47 -0
- data/lib/morpheus/api/monitoring_groups_interface.rb +47 -0
- data/lib/morpheus/api/monitoring_interface.rb +36 -0
- data/lib/morpheus/api/option_type_lists_interface.rb +47 -0
- data/lib/morpheus/api/option_types_interface.rb +47 -0
- data/lib/morpheus/api/roles_interface.rb +0 -1
- data/lib/morpheus/api/setup_interface.rb +19 -9
- data/lib/morpheus/cli.rb +20 -1
- data/lib/morpheus/cli/accounts.rb +8 -3
- data/lib/morpheus/cli/app_templates.rb +19 -11
- data/lib/morpheus/cli/apps.rb +52 -37
- data/lib/morpheus/cli/cli_command.rb +229 -53
- data/lib/morpheus/cli/cli_registry.rb +48 -40
- data/lib/morpheus/cli/clouds.rb +55 -26
- data/lib/morpheus/cli/command_error.rb +12 -0
- data/lib/morpheus/cli/credentials.rb +68 -26
- data/lib/morpheus/cli/curl_command.rb +98 -0
- data/lib/morpheus/cli/dashboard_command.rb +2 -7
- data/lib/morpheus/cli/deployments.rb +4 -4
- data/lib/morpheus/cli/deploys.rb +1 -2
- data/lib/morpheus/cli/dot_file.rb +5 -8
- data/lib/morpheus/cli/error_handler.rb +179 -15
- data/lib/morpheus/cli/groups.rb +21 -13
- data/lib/morpheus/cli/hosts.rb +220 -110
- data/lib/morpheus/cli/instance_types.rb +2 -2
- data/lib/morpheus/cli/instances.rb +257 -167
- data/lib/morpheus/cli/key_pairs.rb +15 -9
- data/lib/morpheus/cli/library.rb +673 -27
- data/lib/morpheus/cli/license.rb +2 -2
- data/lib/morpheus/cli/load_balancers.rb +4 -4
- data/lib/morpheus/cli/log_level_command.rb +6 -4
- data/lib/morpheus/cli/login.rb +17 -3
- data/lib/morpheus/cli/logout.rb +25 -11
- data/lib/morpheus/cli/man_command.rb +388 -0
- data/lib/morpheus/cli/mixins/accounts_helper.rb +1 -1
- data/lib/morpheus/cli/mixins/monitoring_helper.rb +434 -0
- data/lib/morpheus/cli/mixins/print_helper.rb +620 -112
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +1 -1
- data/lib/morpheus/cli/monitoring_apps_command.rb +29 -0
- data/lib/morpheus/cli/monitoring_checks_command.rb +427 -0
- data/lib/morpheus/cli/monitoring_contacts_command.rb +373 -0
- data/lib/morpheus/cli/monitoring_groups_command.rb +29 -0
- data/lib/morpheus/cli/monitoring_incidents_command.rb +711 -0
- data/lib/morpheus/cli/option_types.rb +10 -1
- data/lib/morpheus/cli/recent_activity_command.rb +2 -5
- data/lib/morpheus/cli/remote.rb +874 -134
- data/lib/morpheus/cli/roles.rb +54 -27
- data/lib/morpheus/cli/security_group_rules.rb +2 -2
- data/lib/morpheus/cli/security_groups.rb +23 -19
- data/lib/morpheus/cli/set_prompt_command.rb +50 -0
- data/lib/morpheus/cli/shell.rb +222 -157
- data/lib/morpheus/cli/tasks.rb +19 -15
- data/lib/morpheus/cli/users.rb +27 -17
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/cli/virtual_images.rb +28 -13
- data/lib/morpheus/cli/whoami.rb +131 -52
- data/lib/morpheus/cli/workflows.rb +24 -9
- data/lib/morpheus/formatters.rb +195 -3
- data/lib/morpheus/logging.rb +86 -0
- data/lib/morpheus/terminal.rb +371 -0
- data/scripts/generate_morpheus_commands_help.morpheus +60 -0
- metadata +21 -2
data/lib/morpheus/cli/roles.rb
CHANGED
@@ -60,9 +60,14 @@ class Morpheus::Cli::Roles
|
|
60
60
|
print JSON.pretty_generate(json_response)
|
61
61
|
print "\n"
|
62
62
|
else
|
63
|
-
|
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
|
-
|
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
|
-
|
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,
|
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 =
|
123
|
-
|
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
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
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
|
-
|
144
|
-
|
145
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
170
|
+
print_h1 "Morpheus Security Group Rules for Security Group ID: #{security_group_id}"
|
171
171
|
if rules.empty?
|
172
|
-
|
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
|
-
|
47
|
+
print_h1 "Morpheus Security Groups"
|
48
48
|
if security_groups.empty?
|
49
|
-
|
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
|
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
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
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
|
-
|
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(
|
247
|
-
|
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
|
data/lib/morpheus/cli/shell.rb
CHANGED
@@ -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 ||=
|
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
|
-
|
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? ?
|
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(
|
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
|
-
|
160
|
-
|
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
|
-
|
190
|
-
#
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
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
|
-
|
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
|
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
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
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?(
|
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(
|
367
|
+
Morpheus::Cli::CliRegistry.exec(cmd_name, cmd_args)
|
306
368
|
else
|
307
|
-
|
308
|
-
@history_logger.warn "Unrecognized Command #{
|
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
|
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? ?
|
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
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
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
|
-
|
409
|
+
print char
|
410
|
+
input << char
|
348
411
|
end
|
412
|
+
return input
|
413
|
+
end
|
349
414
|
|
350
|
-
|
351
|
-
|
352
|
-
|
415
|
+
def history_file_path
|
416
|
+
File.join(Morpheus::Cli.home_directory, "shell_history")
|
417
|
+
end
|
353
418
|
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
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
|
-
|
371
|
-
|
372
|
-
|
435
|
+
def load_history_from_log_file(n_commands=1000)
|
436
|
+
@history ||= {}
|
437
|
+
@last_command_number ||= 0
|
373
438
|
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
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
|
-
|
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
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
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
|