morpheus-cli 0.9.9 → 0.9.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/morpheus/api/api_client.rb +4 -0
- data/lib/morpheus/api/app_templates_interface.rb +74 -0
- data/lib/morpheus/api/instance_types_interface.rb +9 -0
- data/lib/morpheus/api/instances_interface.rb +16 -0
- data/lib/morpheus/api/roles_interface.rb +37 -1
- data/lib/morpheus/cli.rb +4 -1
- data/lib/morpheus/cli/accounts.rb +82 -58
- data/lib/morpheus/cli/app_templates.rb +908 -0
- data/lib/morpheus/cli/apps.rb +226 -187
- data/lib/morpheus/cli/cli_command.rb +57 -30
- data/lib/morpheus/cli/clouds.rb +50 -65
- data/lib/morpheus/cli/deployments.rb +18 -33
- data/lib/morpheus/cli/deploys.rb +1 -3
- data/lib/morpheus/cli/groups.rb +54 -38
- data/lib/morpheus/cli/hosts.rb +86 -80
- data/lib/morpheus/cli/instance_types.rb +42 -29
- data/lib/morpheus/cli/instances.rb +192 -69
- data/lib/morpheus/cli/key_pairs.rb +70 -87
- data/lib/morpheus/cli/license.rb +7 -9
- data/lib/morpheus/cli/load_balancers.rb +23 -53
- data/lib/morpheus/cli/mixins/accounts_helper.rb +7 -8
- data/lib/morpheus/cli/mixins/print_helper.rb +67 -0
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +461 -0
- data/lib/morpheus/cli/option_types.rb +71 -18
- data/lib/morpheus/cli/roles.rb +725 -34
- data/lib/morpheus/cli/security_group_rules.rb +50 -70
- data/lib/morpheus/cli/security_groups.rb +61 -48
- data/lib/morpheus/cli/shell.rb +123 -14
- data/lib/morpheus/cli/tasks.rb +24 -59
- data/lib/morpheus/cli/users.rb +86 -71
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/cli/virtual_images.rb +21 -51
- data/lib/morpheus/cli/workflows.rb +14 -29
- data/lib/morpheus/ext/nil_class.rb +5 -0
- data/lib/morpheus/formatters.rb +1 -0
- metadata +7 -3
- data/lib/morpheus/cli/error_handler.rb +0 -44
@@ -1,7 +1,6 @@
|
|
1
1
|
# require 'yaml'
|
2
2
|
require 'io/console'
|
3
3
|
require 'rest_client'
|
4
|
-
require 'term/ansicolor'
|
5
4
|
require 'optparse'
|
6
5
|
require 'filesize'
|
7
6
|
require 'table_print'
|
@@ -9,7 +8,7 @@ require 'morpheus/cli/cli_command'
|
|
9
8
|
|
10
9
|
class Morpheus::Cli::SecurityGroupRules
|
11
10
|
include Morpheus::Cli::CliCommand
|
12
|
-
|
11
|
+
|
13
12
|
def initialize()
|
14
13
|
@appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
|
15
14
|
@access_token = Morpheus::Cli::Credentials.new(@appliance_name,@appliance_url).request_credentials()
|
@@ -20,7 +19,7 @@ class Morpheus::Cli::SecurityGroupRules
|
|
20
19
|
|
21
20
|
def handle(args)
|
22
21
|
if @access_token.empty?
|
23
|
-
|
22
|
+
print_red_alert "Invalid Credentials. Unable to acquire access token. Please verify your credentials and try again."
|
24
23
|
return 1
|
25
24
|
end
|
26
25
|
if args.empty?
|
@@ -49,25 +48,22 @@ Usage: morpheus security-group-rules add_custom_rule SOURCE_CIDR PORT_RANGE PROT
|
|
49
48
|
PORT_RANGE: Port value (i.e. 123) or port range (i.e. 1-65535)
|
50
49
|
PROTOCOL: tcp, udp, icmp\n\n'
|
51
50
|
EOT
|
52
|
-
|
53
|
-
if args.count < 3
|
54
|
-
puts usage
|
55
|
-
return
|
56
|
-
end
|
57
|
-
|
51
|
+
options = {}
|
58
52
|
security_group_id = nil
|
59
53
|
optparse = OptionParser.new do|opts|
|
60
|
-
opts.banner =
|
54
|
+
opts.banner = usage
|
61
55
|
opts.on( '-s', '--secgroup SECGROUP', "Security Group ID (Use will use security as set with 'security-groups use id'" ) do |id|
|
62
56
|
security_group_id = id
|
63
57
|
end
|
64
|
-
opts
|
65
|
-
puts opts
|
66
|
-
exit
|
67
|
-
end
|
58
|
+
build_common_options(opts, options, [])
|
68
59
|
end
|
69
60
|
optparse.parse(args)
|
70
61
|
|
62
|
+
if args.count < 3
|
63
|
+
puts "\n#{optparse.banner}\n\n"
|
64
|
+
exit 1
|
65
|
+
end
|
66
|
+
|
71
67
|
if security_group_id.nil?
|
72
68
|
security_group_id = @active_security_group[@appliance_name.to_sym]
|
73
69
|
end
|
@@ -77,7 +73,7 @@ EOT
|
|
77
73
|
exit
|
78
74
|
end
|
79
75
|
|
80
|
-
|
76
|
+
params = {
|
81
77
|
:rule => {
|
82
78
|
:source => args[0],
|
83
79
|
:portRange => args[1],
|
@@ -87,17 +83,12 @@ EOT
|
|
87
83
|
}
|
88
84
|
|
89
85
|
begin
|
90
|
-
@security_group_rules_interface.create(security_group_id,
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
else
|
96
|
-
puts "Error Communicating with the Appliance. Please try again later. #{e}"
|
97
|
-
end
|
98
|
-
return nil
|
86
|
+
@security_group_rules_interface.create(security_group_id, params)
|
87
|
+
list([])
|
88
|
+
rescue RestClient::Exception => e
|
89
|
+
print_rest_exception(e, options)
|
90
|
+
exit 1
|
99
91
|
end
|
100
|
-
list([])
|
101
92
|
end
|
102
93
|
|
103
94
|
def add_instance_rule(args)
|
@@ -106,24 +97,21 @@ Usage: morpheus security-group-rules add_instance_rule SOURCE_CIDR INSTANCE_TYPE
|
|
106
97
|
SOURCE_CIDR: CIDR to white-list
|
107
98
|
INSTANCE_TYPE_ID: ID of the Instance Type to access
|
108
99
|
EOT
|
109
|
-
if args.count < 2
|
110
|
-
puts usage
|
111
|
-
return
|
112
|
-
end
|
113
100
|
|
101
|
+
options = {}
|
114
102
|
security_group_id = nil
|
115
103
|
optparse = OptionParser.new do|opts|
|
116
|
-
opts.banner =
|
104
|
+
opts.banner = usage
|
117
105
|
opts.on( '-s', '--secgroup secgroup', "Security Group ID (Use will use security as set with 'security-groups use id'" ) do |id|
|
118
106
|
security_group_id = id
|
119
107
|
end
|
120
|
-
opts
|
121
|
-
puts opts
|
122
|
-
exit
|
123
|
-
end
|
108
|
+
build_common_options(opts, options, [])
|
124
109
|
end
|
125
110
|
optparse.parse(args)
|
126
|
-
|
111
|
+
if args.count < 2
|
112
|
+
puts "\n#{optparse.banner}\n\n"
|
113
|
+
exit 1
|
114
|
+
end
|
127
115
|
if security_group_id.nil?
|
128
116
|
security_group_id = @active_security_group[@appliance_name.to_sym]
|
129
117
|
end
|
@@ -133,7 +121,7 @@ EOT
|
|
133
121
|
exit
|
134
122
|
end
|
135
123
|
|
136
|
-
|
124
|
+
params = {
|
137
125
|
:rule => {
|
138
126
|
:source => args[0],
|
139
127
|
:instanceTypeId => args[1]
|
@@ -141,17 +129,12 @@ EOT
|
|
141
129
|
}
|
142
130
|
|
143
131
|
begin
|
144
|
-
@security_group_rules_interface.create(security_group_id,
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
else
|
150
|
-
puts "Error Communicating with the Appliance. Please try again later. #{e}"
|
151
|
-
end
|
152
|
-
return nil
|
132
|
+
@security_group_rules_interface.create(security_group_id, params)
|
133
|
+
list([security_group_id])
|
134
|
+
rescue RestClient::Exception => e
|
135
|
+
print_rest_exception(e, options)
|
136
|
+
exit 1
|
153
137
|
end
|
154
|
-
list([])
|
155
138
|
end
|
156
139
|
|
157
140
|
def list(args)
|
@@ -159,7 +142,7 @@ EOT
|
|
159
142
|
security_group_id = nil
|
160
143
|
optparse = OptionParser.new do|opts|
|
161
144
|
opts.banner = "\nUsage: morpheus security-group-rules list [ID]"
|
162
|
-
|
145
|
+
build_common_options(opts, options, [:json])
|
163
146
|
end
|
164
147
|
security_group_id = args[0].to_i
|
165
148
|
optparse.parse(args)
|
@@ -175,7 +158,12 @@ EOT
|
|
175
158
|
|
176
159
|
begin
|
177
160
|
params = {}
|
178
|
-
json_response = @security_group_rules_interface.get(security_group_id,
|
161
|
+
json_response = @security_group_rules_interface.get(security_group_id, params)
|
162
|
+
if options[:json]
|
163
|
+
print JSON.pretty_generate(json_response)
|
164
|
+
print "\n"
|
165
|
+
return
|
166
|
+
end
|
179
167
|
rules = json_response['rules']
|
180
168
|
print "\n" ,cyan, bold, "Morpheus Security Group Rules for Security Group ID:#{security_group_id}\n","==================", reset, "\n\n"
|
181
169
|
if rules.empty?
|
@@ -187,31 +175,28 @@ EOT
|
|
187
175
|
end
|
188
176
|
print reset,"\n\n"
|
189
177
|
|
190
|
-
rescue => e
|
191
|
-
|
192
|
-
|
178
|
+
rescue RestClient::Exception => e
|
179
|
+
print_rest_exception(e, options)
|
180
|
+
exit 1
|
193
181
|
end
|
194
182
|
end
|
195
183
|
|
196
184
|
def remove(args)
|
197
|
-
|
198
|
-
|
199
|
-
return
|
200
|
-
end
|
201
|
-
|
185
|
+
|
186
|
+
options = {}
|
202
187
|
security_group_id = nil
|
203
188
|
optparse = OptionParser.new do|opts|
|
204
|
-
opts.banner = "
|
189
|
+
opts.banner = "Usage: morpheus security-group-rules remove ID [options]"
|
205
190
|
opts.on( '-s', '--secgroup secgroup', "Security Group ID (Use will use security as set with 'security-groups use id'" ) do |id|
|
206
191
|
security_group_id = id
|
207
192
|
end
|
208
|
-
opts
|
209
|
-
puts opts
|
210
|
-
exit
|
211
|
-
end
|
193
|
+
build_common_options(opts, options, [])
|
212
194
|
end
|
213
195
|
optparse.parse(args)
|
214
|
-
|
196
|
+
if args.count < 1
|
197
|
+
puts "\n#{optparse.banner}\n\n"
|
198
|
+
exit 1
|
199
|
+
end
|
215
200
|
if security_group_id.nil?
|
216
201
|
security_group_id = @active_security_group[@appliance_name.to_sym]
|
217
202
|
end
|
@@ -223,15 +208,10 @@ EOT
|
|
223
208
|
|
224
209
|
begin
|
225
210
|
@security_group_rules_interface.delete(security_group_id, args[0])
|
226
|
-
list([])
|
211
|
+
list([security_group_id])
|
227
212
|
rescue RestClient::Exception => e
|
228
|
-
|
229
|
-
|
230
|
-
::Morpheus::Cli::ErrorHandler.new.print_errors(error)
|
231
|
-
else
|
232
|
-
puts "Error Communicating with the Appliance. Please try again later. #{e}"
|
233
|
-
end
|
234
|
-
return nil
|
213
|
+
print_rest_exception(e, options)
|
214
|
+
exit 1
|
235
215
|
end
|
236
216
|
end
|
237
217
|
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# require 'yaml'
|
2
2
|
require 'io/console'
|
3
3
|
require 'rest_client'
|
4
|
-
require 'term/ansicolor'
|
5
4
|
require 'optparse'
|
6
5
|
require 'filesize'
|
7
6
|
require 'table_print'
|
@@ -9,7 +8,7 @@ require 'morpheus/cli/cli_command'
|
|
9
8
|
|
10
9
|
class Morpheus::Cli::SecurityGroups
|
11
10
|
include Morpheus::Cli::CliCommand
|
12
|
-
|
11
|
+
|
13
12
|
def initialize()
|
14
13
|
@appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
|
15
14
|
@access_token = Morpheus::Cli::Credentials.new(@appliance_name,@appliance_url).request_credentials()
|
@@ -20,7 +19,7 @@ class Morpheus::Cli::SecurityGroups
|
|
20
19
|
|
21
20
|
def handle(args)
|
22
21
|
if @access_token.empty?
|
23
|
-
|
22
|
+
print_red_alert "Invalid Credentials. Unable to acquire access token. Please verify your credentials and try again."
|
24
23
|
return 1
|
25
24
|
end
|
26
25
|
if args.empty?
|
@@ -45,6 +44,12 @@ class Morpheus::Cli::SecurityGroups
|
|
45
44
|
end
|
46
45
|
|
47
46
|
def list(args)
|
47
|
+
options = {}
|
48
|
+
optparse = OptionParser.new do|opts|
|
49
|
+
opts.banner = "\nUsage: morpheus security-groups list"
|
50
|
+
build_common_options(opts, options, [])
|
51
|
+
end
|
52
|
+
optparse.parse(args)
|
48
53
|
begin
|
49
54
|
json_response = @security_groups_interface.list()
|
50
55
|
security_groups = json_response['securityGroups']
|
@@ -53,21 +58,32 @@ class Morpheus::Cli::SecurityGroups
|
|
53
58
|
puts yellow,"No Security Groups currently configured.",reset
|
54
59
|
else
|
55
60
|
security_groups.each do |security_group|
|
56
|
-
|
61
|
+
|
62
|
+
if @active_security_group[@appliance_name.to_sym] = security_group['id']
|
63
|
+
print cyan, "=> #{security_group['id']}: #{security_group['name']} (#{security_group['description']})\n"
|
64
|
+
else
|
65
|
+
print cyan, "= #{security_group['id']}: #{security_group['name']} (#{security_group['description']})\n"
|
66
|
+
end
|
57
67
|
end
|
58
68
|
end
|
59
69
|
print reset,"\n\n"
|
60
70
|
|
61
|
-
rescue => e
|
62
|
-
|
63
|
-
|
71
|
+
rescue RestClient::Exception => e
|
72
|
+
print_rest_exception(e, options)
|
73
|
+
exit 1
|
64
74
|
end
|
65
75
|
end
|
66
76
|
|
67
77
|
def get(args)
|
78
|
+
options = {}
|
79
|
+
optparse = OptionParser.new do|opts|
|
80
|
+
opts.banner = "Usage: morpheus security-groups get ID"
|
81
|
+
build_common_options(opts, options, [])
|
82
|
+
end
|
83
|
+
optparse.parse(args)
|
68
84
|
if args.count < 1
|
69
|
-
puts "\
|
70
|
-
|
85
|
+
puts "\n#{optparse.banner}\n\n"
|
86
|
+
exit 1
|
71
87
|
end
|
72
88
|
|
73
89
|
begin
|
@@ -81,49 +97,45 @@ class Morpheus::Cli::SecurityGroups
|
|
81
97
|
end
|
82
98
|
print reset,"\n\n"
|
83
99
|
|
84
|
-
rescue => e
|
85
|
-
|
86
|
-
|
100
|
+
rescue RestClient::Exception => e
|
101
|
+
print_rest_exception(e, options)
|
102
|
+
exit 1
|
87
103
|
end
|
88
104
|
end
|
89
105
|
|
90
106
|
def add(args)
|
91
|
-
|
92
|
-
|
93
|
-
return
|
94
|
-
end
|
95
|
-
|
96
|
-
options = {:securityGroup => {:name => args[0]} }
|
107
|
+
options = {}
|
108
|
+
params = {:securityGroup => {:name => args[0]} }
|
97
109
|
optparse = OptionParser.new do|opts|
|
98
110
|
opts.banner = "\nUsage: morpheus security-groups add NAME [options]"
|
99
111
|
opts.on( '-d', '--description Description', "Description of the security group" ) do |description|
|
100
|
-
|
101
|
-
end
|
102
|
-
|
103
|
-
opts.on( '-h', '--help', "Prints this help" ) do
|
104
|
-
puts opts
|
105
|
-
exit
|
112
|
+
params[:securityGroup][:description] = description
|
106
113
|
end
|
114
|
+
build_common_options(opts, options, [])
|
107
115
|
end
|
108
116
|
optparse.parse(args)
|
109
|
-
|
117
|
+
if args.count < 1
|
118
|
+
puts "\n#{optparse.banner}\n\n"
|
119
|
+
exit 1
|
120
|
+
end
|
110
121
|
begin
|
111
|
-
@security_groups_interface.create(
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
else
|
117
|
-
puts "Error Communicating with the Appliance. Please try again later. #{e}"
|
118
|
-
end
|
119
|
-
return nil
|
122
|
+
@security_groups_interface.create(params)
|
123
|
+
list([])
|
124
|
+
rescue RestClient::Exception => e
|
125
|
+
print_rest_exception(e, options)
|
126
|
+
exit 1
|
120
127
|
end
|
121
|
-
list([])
|
122
128
|
end
|
123
129
|
|
124
130
|
def remove(args)
|
131
|
+
options = {}
|
132
|
+
optparse = OptionParser.new do|opts|
|
133
|
+
opts.banner = "\nUsage: morpheus security-groups remove ID"
|
134
|
+
build_common_options(opts, options, [])
|
135
|
+
end
|
136
|
+
optparse.parse(args)
|
125
137
|
if args.count < 1
|
126
|
-
puts "\
|
138
|
+
puts "\n#{optparse.banner}\n\n"
|
127
139
|
return
|
128
140
|
end
|
129
141
|
begin
|
@@ -136,19 +148,20 @@ class Morpheus::Cli::SecurityGroups
|
|
136
148
|
@security_groups_interface.delete(security_group['id'])
|
137
149
|
list([])
|
138
150
|
rescue RestClient::Exception => e
|
139
|
-
|
140
|
-
|
141
|
-
::Morpheus::Cli::ErrorHandler.new.print_errors(error)
|
142
|
-
else
|
143
|
-
puts "Error Communicating with the Appliance. Please try again later. #{e}"
|
144
|
-
end
|
145
|
-
return nil
|
151
|
+
print_rest_exception(e, options)
|
152
|
+
exit 1
|
146
153
|
end
|
147
154
|
end
|
148
155
|
|
149
|
-
def use(args)
|
156
|
+
def use(args)
|
157
|
+
options = {}
|
158
|
+
optparse = OptionParser.new do|opts|
|
159
|
+
opts.banner = "Usage: morpheus security-groups use ID"
|
160
|
+
build_common_options(opts, options, [])
|
161
|
+
end
|
162
|
+
optparse.parse(args)
|
150
163
|
if args.length < 1
|
151
|
-
puts "
|
164
|
+
puts "\n#{optparse.banner}\n\n"
|
152
165
|
return
|
153
166
|
end
|
154
167
|
begin
|
@@ -161,9 +174,9 @@ class Morpheus::Cli::SecurityGroups
|
|
161
174
|
else
|
162
175
|
puts "Security Group not found"
|
163
176
|
end
|
164
|
-
rescue => e
|
165
|
-
|
166
|
-
|
177
|
+
rescue RestClient::Exception => e
|
178
|
+
print_rest_exception(e, options)
|
179
|
+
exit 1
|
167
180
|
end
|
168
181
|
end
|
169
182
|
|
data/lib/morpheus/cli/shell.rb
CHANGED
@@ -7,6 +7,8 @@ require 'table_print'
|
|
7
7
|
require 'morpheus/cli/cli_command'
|
8
8
|
require "shellwords"
|
9
9
|
require 'readline'
|
10
|
+
require 'logger'
|
11
|
+
require 'fileutils'
|
10
12
|
|
11
13
|
|
12
14
|
class Morpheus::Cli::Shell
|
@@ -15,7 +17,8 @@ class Morpheus::Cli::Shell
|
|
15
17
|
def initialize()
|
16
18
|
@appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
|
17
19
|
@auto_complete = proc do |s|
|
18
|
-
command_list = Morpheus::Cli::CliRegistry.all.keys
|
20
|
+
command_list = Morpheus::Cli::CliRegistry.all.keys.reject {|k| [:shell].include?(k) }
|
21
|
+
command_list += [:clear, :history, :flush_history, :exit]
|
19
22
|
result = command_list.grep(/^#{Regexp.escape(s)}/)
|
20
23
|
if result.nil? || result.empty?
|
21
24
|
Readline::FILENAME_COMPLETION_PROC.call(s)
|
@@ -24,7 +27,6 @@ class Morpheus::Cli::Shell
|
|
24
27
|
end
|
25
28
|
end
|
26
29
|
|
27
|
-
|
28
30
|
end
|
29
31
|
|
30
32
|
def handle(args)
|
@@ -49,7 +51,10 @@ class Morpheus::Cli::Shell
|
|
49
51
|
end
|
50
52
|
optparse.parse(args)
|
51
53
|
|
52
|
-
|
54
|
+
@history_logger ||= load_history_logger
|
55
|
+
@history_logger.info "shell started" if @history_logger
|
56
|
+
load_history_from_log_file()
|
57
|
+
|
53
58
|
remote_handler = Morpheus::Cli::Remote.new()
|
54
59
|
exit = false
|
55
60
|
while !exit do
|
@@ -57,28 +62,56 @@ class Morpheus::Cli::Shell
|
|
57
62
|
Readline.completion_proc = @auto_complete
|
58
63
|
Readline.basic_word_break_characters = "\t\n\"\‘`@$><=;|&{( "
|
59
64
|
input = Readline.readline("#{cyan}morpheus> #{reset}", true).to_s
|
65
|
+
input = input.strip
|
60
66
|
# print cyan,"morpheus > ",reset
|
61
67
|
# input = $stdin.gets.chomp!
|
62
68
|
if !input.empty?
|
63
69
|
|
64
70
|
if input == 'exit'
|
71
|
+
@history_logger.info "exit" if @history_logger
|
65
72
|
break
|
66
|
-
elsif input
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
73
|
+
elsif input =~ /^history/
|
74
|
+
n_commands = input.sub(/^history\s?/, '').sub(/\-n\s?/, '')
|
75
|
+
n_commands = n_commands.empty? ? 25 : n_commands.to_i
|
76
|
+
cmd_numbers = @history.keys.last(n_commands)
|
77
|
+
puts "Last #{cmd_numbers.size} commands"
|
78
|
+
cmd_numbers.each do |cmd_number|
|
79
|
+
cmd = @history[cmd_number]
|
80
|
+
puts "#{cmd_number.to_s.rjust(3, ' ')} #{cmd}"
|
71
81
|
end
|
72
82
|
next
|
73
83
|
elsif input == 'clear'
|
74
84
|
print "\e[H\e[2J"
|
75
85
|
next
|
76
|
-
elsif input == '
|
77
|
-
|
86
|
+
elsif input == 'flush_history'
|
87
|
+
file_path = history_file_path
|
88
|
+
if File.exists?(file_path)
|
89
|
+
File.truncate(file_path, 0)
|
90
|
+
end
|
91
|
+
@history = {}
|
92
|
+
@last_command_number = 0
|
93
|
+
@history_logger = load_history_logger
|
94
|
+
puts "history cleared!"
|
95
|
+
next
|
96
|
+
elsif input == '!!'
|
97
|
+
cmd_number = @history.keys[-1]
|
98
|
+
input = @history[cmd_number]
|
99
|
+
if !input
|
100
|
+
puts "There is no previous command"
|
101
|
+
next
|
102
|
+
end
|
103
|
+
elsif input =~ /^\!.+/
|
104
|
+
cmd_number = input.sub("!", "").to_i
|
105
|
+
if cmd_number != 0
|
106
|
+
input = @history[cmd_number]
|
107
|
+
if !input
|
108
|
+
puts "Command not found by number #{cmd_index}"
|
109
|
+
next
|
110
|
+
end
|
111
|
+
end
|
78
112
|
end
|
79
113
|
|
80
114
|
begin
|
81
|
-
history << input
|
82
115
|
argv = Shellwords.shellsplit(input)
|
83
116
|
if @command_options[:account_name]
|
84
117
|
argv.push "--account", @command_options[:account_name]
|
@@ -90,19 +123,31 @@ class Morpheus::Cli::Shell
|
|
90
123
|
argv.push "--nocolor"
|
91
124
|
end
|
92
125
|
#puts "cmd: #{argv.join(' ')}"
|
93
|
-
|
126
|
+
|
127
|
+
if argv[0] == 'shell'
|
128
|
+
puts "Unrecognized Command."
|
129
|
+
elsif argv[0] == 'remote'
|
130
|
+
log_history_command(input)
|
94
131
|
remote_handler.handle(argv[1..-1])
|
95
132
|
elsif Morpheus::Cli::CliRegistry.has_command?(argv[0])
|
133
|
+
log_history_command(input)
|
96
134
|
Morpheus::Cli::CliRegistry.exec(argv[0], argv[1..-1])
|
97
135
|
else
|
136
|
+
@history_logger.warn "Unrecognized Command: #{input}" if @history_logger
|
98
137
|
puts "Unrecognized Command."
|
99
138
|
end
|
100
139
|
rescue ArgumentError
|
101
140
|
puts "Argument Syntax Error..."
|
102
|
-
rescue
|
103
|
-
|
141
|
+
rescue Interrupt
|
142
|
+
# nothing to do
|
143
|
+
@history_logger.warn "shell interrupt" if @history_logger
|
144
|
+
print "\n"
|
145
|
+
rescue SystemExit
|
146
|
+
# nothing to do
|
104
147
|
print "\n"
|
105
148
|
rescue => e
|
149
|
+
|
150
|
+
@history_logger.error "#{e.message}" if @history_logger
|
106
151
|
print red, "\n", e.message, "\n", reset
|
107
152
|
print e.backtrace.join("\n"), "\n"
|
108
153
|
end
|
@@ -125,4 +170,68 @@ class Morpheus::Cli::Shell
|
|
125
170
|
end
|
126
171
|
return input
|
127
172
|
end
|
173
|
+
|
174
|
+
def history_file_path
|
175
|
+
File.join(Dir.home, '.morpheus', "shell_history")
|
176
|
+
end
|
177
|
+
|
178
|
+
def load_history_logger
|
179
|
+
file_path = history_file_path
|
180
|
+
if !Dir.exists?(File.dirname(file_path))
|
181
|
+
FileUtils.mkdir_p(File.dirname(file_path))
|
182
|
+
end
|
183
|
+
logger = Logger.new(file_path)
|
184
|
+
# logger.formatter = proc do |severity, datetime, progname, msg|
|
185
|
+
# "#{msg}\n"
|
186
|
+
# end
|
187
|
+
return logger
|
188
|
+
end
|
189
|
+
|
190
|
+
def load_history_from_log_file(n_commands=1000)
|
191
|
+
@history ||= {}
|
192
|
+
@last_command_number ||= 0
|
193
|
+
|
194
|
+
if Gem.win_platform?
|
195
|
+
return @history
|
196
|
+
end
|
197
|
+
|
198
|
+
begin
|
199
|
+
file_path = history_file_path
|
200
|
+
FileUtils.mkdir_p(File.dirname(file_path))
|
201
|
+
# grab extra lines because not all log entries are commands
|
202
|
+
n_lines = n_commands + 500
|
203
|
+
history_lines = `tail -n #{n_lines} #{file_path}`.split(/\n/)
|
204
|
+
command_lines = history_lines.select do |line|
|
205
|
+
line.match(/\(cmd (\d+)\) (.+)/)
|
206
|
+
end
|
207
|
+
command_lines = command_lines.last(n_commands)
|
208
|
+
command_lines.each do |line|
|
209
|
+
matches = line.match(/\(cmd (\d+)\) (.+)/)
|
210
|
+
if matches && matches.size == 3
|
211
|
+
cmd_number = matches[1].to_i
|
212
|
+
cmd = matches[2]
|
213
|
+
|
214
|
+
@last_command_number = cmd_number
|
215
|
+
@history[@last_command_number] = cmd
|
216
|
+
|
217
|
+
# for Ctrl+R history searching
|
218
|
+
Readline::HISTORY << cmd
|
219
|
+
end
|
220
|
+
end
|
221
|
+
rescue => e
|
222
|
+
raise e
|
223
|
+
end
|
224
|
+
|
225
|
+
return @history
|
226
|
+
end
|
227
|
+
|
228
|
+
def log_history_command(cmd)
|
229
|
+
@history ||= {}
|
230
|
+
@last_command_number ||= 0
|
231
|
+
@last_command_number += 1
|
232
|
+
@history[@last_command_number] = cmd
|
233
|
+
if @history_logger
|
234
|
+
@history_logger.info "(cmd #{@last_command_number}) #{cmd}"
|
235
|
+
end
|
236
|
+
end
|
128
237
|
end
|