net-ssh-cli 1.9.1 → 1.9.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/net/ssh/cli/version.rb +1 -1
- data/lib/net/ssh/cli.rb +70 -73
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0e1db663e9e79a41c9c5fbb7b0dce5918a0bd61b83fb98f8d9314f1a4602a698
|
4
|
+
data.tar.gz: d7841ea18aefa288771a8ef64459373824b2c307b5f4b8e89a0b636fdfceb172
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5e33f5fd908ea9c927913692add3a4a112359e85875eb64af988c4f8d9b6d0fc6e3fa877583ec5be8ebb2f6dd62c83c2c5eebd8df858d07897e08c62bfa0452e
|
7
|
+
data.tar.gz: 92007ac57dd811683cc6715592c2544ab28e7a0be42237e65f8d3d71ed44e2c3d2df815d1bb6395b1f73294e00529b6a5f8a11cd257396b6a2623b34dcab7126
|
data/lib/net/ssh/cli/version.rb
CHANGED
data/lib/net/ssh/cli.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
3
|
+
require "net/ssh/cli/version"
|
4
|
+
require "net/ssh"
|
5
|
+
require "active_support/core_ext/hash/indifferent_access"
|
6
|
+
require "active_support/core_ext/object/blank"
|
7
|
+
require "timeout"
|
8
|
+
require "logger"
|
9
9
|
|
10
10
|
module Net
|
11
11
|
module SSH
|
@@ -40,36 +40,36 @@ module Net
|
|
40
40
|
attr_accessor :channel, :stdout, :net_ssh, :logger, :new_data, :process_count
|
41
41
|
|
42
42
|
OPTIONS = ActiveSupport::HashWithIndifferentAccess.new(
|
43
|
-
default_prompt:
|
44
|
-
cmd_rm_prompt:
|
45
|
-
cmd_rm_command:
|
46
|
-
cmd_rm_command_tail:
|
47
|
-
cmd_minimum_duration:
|
48
|
-
run_impact:
|
49
|
-
read_till_timeout:
|
50
|
-
read_till_hard_timeout:
|
51
|
-
read_till_hard_timeout_factor: 1.2,
|
52
|
-
named_prompts:
|
53
|
-
before_cmd_procs:
|
54
|
-
after_cmd_procs:
|
55
|
-
before_on_stdout_procs:
|
56
|
-
after_on_stdout_procs:
|
57
|
-
before_on_stdin_procs:
|
58
|
-
after_on_stdin_procs:
|
43
|
+
default_prompt: /\n?^(\S+@.*)\z/, # the default prompt to search for. It is recommended to use \z to ensure you don't match the prompt too early.
|
44
|
+
cmd_rm_prompt: false, # whether the prompt should be removed in the output of #cmd
|
45
|
+
cmd_rm_command: false, # whether the given command should be removed in the output of #cmd
|
46
|
+
cmd_rm_command_tail: "\n", # which format does the end of line return after a command has been submitted. Could be something like "ls\n" "ls\r\n" or "ls \n" (extra spaces)
|
47
|
+
cmd_minimum_duration: 0, # how long do you want to wait/sleep after sending the command. After this waiting time, the output will be processed and the prompt will be searched.
|
48
|
+
run_impact: false, # whether to run #impact commands. This might align with testing|development|production. example #impact("reboot")
|
49
|
+
read_till_timeout: nil, # timeout for #read_till to find the match
|
50
|
+
read_till_hard_timeout: nil, # hard timeout for #read_till to find the match using Timeout.timeout(hard_timeout) {}. Might creates unpredicted sideffects
|
51
|
+
read_till_hard_timeout_factor: 1.2, # hard timeout factor in case read_till_hard_timeout is true
|
52
|
+
named_prompts: ActiveSupport::HashWithIndifferentAccess.new, # you can used named prompts for #with_prompt {}
|
53
|
+
before_cmd_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call before #cmd
|
54
|
+
after_cmd_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call after #cmd
|
55
|
+
before_on_stdout_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call before data arrives from the underlying connection
|
56
|
+
after_on_stdout_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call after data arrives from the underlying connection
|
57
|
+
before_on_stdin_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call before data is sent to the underlying channel
|
58
|
+
after_on_stdin_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call after data is sent to the underlying channel
|
59
59
|
before_open_channel_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call before opening a channel
|
60
|
-
after_open_channel_procs:
|
61
|
-
open_channel_timeout:
|
62
|
-
net_ssh_options:
|
63
|
-
process_time:
|
64
|
-
background_processing:
|
65
|
-
on_stdout_processing:
|
66
|
-
sleep_procs:
|
67
|
-
terminal_chars_width:
|
68
|
-
terminal_chars_height:
|
69
|
-
terminal_pixels_width:
|
70
|
-
terminal_pixels_height:
|
71
|
-
terminal_term:
|
72
|
-
terminal_modes:
|
60
|
+
after_open_channel_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call after opening a channel, for example you could call #detect_prompt or #read_till
|
61
|
+
open_channel_timeout: nil, # timeout to open the channel
|
62
|
+
net_ssh_options: ActiveSupport::HashWithIndifferentAccess.new, # a wrapper for options to pass to Net::SSH.start in case net_ssh is undefined
|
63
|
+
process_time: 0.00001, # how long #process is processing net_ssh#process or sleeping (waiting for something)
|
64
|
+
background_processing: false, # default false, whether the process method maps to the underlying net_ssh#process or the net_ssh#process happens in a separate loop
|
65
|
+
on_stdout_processing: 100, # whether to optimize the on_stdout performance by calling #process #optimize_on_stdout-times in case more data arrives
|
66
|
+
sleep_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call instead of Kernel.sleep(), perfect for async hooks
|
67
|
+
terminal_chars_width: 320, # Sets and sends the terminal dimensions during the opening of the channel. It does not send a channel_request on change.
|
68
|
+
terminal_chars_height: 120, # See also https://github.com/net-ssh/net-ssh/blob/master/lib/net/ssh/connection/channel.rb#L220 section 'def request_pty'
|
69
|
+
terminal_pixels_width: 1920, # See also https://www.ietf.org/rfc/rfc4254.txt section pty-req and section window-change
|
70
|
+
terminal_pixels_height: 1080, #
|
71
|
+
terminal_term: nil, # Sets the terminal term, usually xterm
|
72
|
+
terminal_modes: nil #
|
73
73
|
)
|
74
74
|
|
75
75
|
def options
|
@@ -103,10 +103,10 @@ module Net
|
|
103
103
|
end
|
104
104
|
end
|
105
105
|
|
106
|
-
OPTIONS.keys.select {|key| key.to_s.include? "procs"}.each do |name|
|
107
|
-
define_method name.sub("_procs","") do |&blk|
|
106
|
+
OPTIONS.keys.select { |key| key.to_s.include? "procs" }.each do |name|
|
107
|
+
define_method name.sub("_procs", "") do |&blk|
|
108
108
|
id = SecureRandom.uuid
|
109
|
-
|
109
|
+
send(name)[id] = proc { blk.call }
|
110
110
|
id
|
111
111
|
end
|
112
112
|
end
|
@@ -168,12 +168,10 @@ module Net
|
|
168
168
|
# end
|
169
169
|
# cmd("exit")
|
170
170
|
#
|
171
|
-
def with_named_prompt(name)
|
171
|
+
def with_named_prompt(name, &block)
|
172
172
|
raise Error::UndefinedMatch, "unknown named_prompt #{name}" unless named_prompts[name]
|
173
173
|
|
174
|
-
with_prompt(named_prompts[name])
|
175
|
-
yield
|
176
|
-
end
|
174
|
+
with_prompt(named_prompts[name], &block)
|
177
175
|
end
|
178
176
|
|
179
177
|
# tries to detect the prompt
|
@@ -217,17 +215,17 @@ module Net
|
|
217
215
|
# - hard_timeout_factor: nil, true, or a number
|
218
216
|
# - when hard_timeout == true, this will set the hard_timeout as (read_till_hard_timeout_factor * read_till_timeout), defaults to 1.2 = +20%
|
219
217
|
def read_till(prompt: current_prompt, timeout: read_till_timeout, hard_timeout: read_till_hard_timeout, hard_timeout_factor: read_till_hard_timeout_factor, **_opts)
|
220
|
-
raise Error::UndefinedMatch,
|
218
|
+
raise Error::UndefinedMatch, "no prompt given or default_prompt defined" unless prompt
|
219
|
+
|
221
220
|
hard_timeout = (read_till_hard_timeout_factor * timeout) if timeout and hard_timeout == true
|
222
221
|
hard_timeout = nil if hard_timeout == true
|
223
222
|
|
224
223
|
with_prompt(prompt) do
|
225
224
|
::Timeout.timeout(hard_timeout, Error::ReadTillTimeout, "#{current_prompt.inspect} didn't match on #{stdout.inspect} within #{hard_timeout}s") do
|
226
225
|
soft_timeout = Time.now + timeout if timeout
|
227
|
-
until prompt_in_stdout?
|
228
|
-
if timeout and soft_timeout < Time.now
|
229
|
-
|
230
|
-
end
|
226
|
+
until prompt_in_stdout?
|
227
|
+
raise Error::ReadTillTimeout, "#{current_prompt.inspect} didn't match on #{stdout.inspect} within #{timeout}s" if timeout and soft_timeout < Time.now
|
228
|
+
|
231
229
|
process
|
232
230
|
sleep 0.01 # don't race for CPU
|
233
231
|
end
|
@@ -253,7 +251,7 @@ module Net
|
|
253
251
|
end
|
254
252
|
|
255
253
|
def dialog(command, prompt, **opts)
|
256
|
-
opts = opts.clone.merge(prompt:
|
254
|
+
opts = opts.clone.merge(prompt:)
|
257
255
|
cmd(command, **opts)
|
258
256
|
end
|
259
257
|
|
@@ -265,7 +263,7 @@ module Net
|
|
265
263
|
# Hint: 'read' first on purpose as a feature. once you cmd you ignore what happend before. otherwise use read|write directly.
|
266
264
|
# this should avoid many horrible state issues where the prompt is not the last prompt
|
267
265
|
def cmd(command, pre_read: true, rm_prompt: cmd_rm_prompt, rm_command: cmd_rm_command, prompt: current_prompt, minimum_duration: cmd_minimum_duration, **opts)
|
268
|
-
opts = opts.clone.merge(pre_read
|
266
|
+
opts = opts.clone.merge(pre_read:, rm_prompt:, rm_command:, prompt:)
|
269
267
|
if pre_read
|
270
268
|
pre_read_data = read
|
271
269
|
logger.debug { "#cmd ignoring pre-command output: #{pre_read_data.inspect}" } if pre_read_data.present?
|
@@ -291,7 +289,7 @@ module Net
|
|
291
289
|
alias commands cmds
|
292
290
|
|
293
291
|
def rm_command!(output, command, **opts)
|
294
|
-
output[command + cmd_rm_command_tail] =
|
292
|
+
output[command + cmd_rm_command_tail] = "" if rm_command?(**opts) && output[command + cmd_rm_command_tail]
|
295
293
|
end
|
296
294
|
|
297
295
|
# removes the prompt from the given output
|
@@ -299,19 +297,17 @@ module Net
|
|
299
297
|
# for backwards compatibility it also tries to replace the first match of the prompt /(something)\z/
|
300
298
|
# it removes the whole match if no matches are given /something\z/
|
301
299
|
def rm_prompt!(output, prompt: current_prompt, **opts)
|
302
|
-
if rm_prompt?(**opts)
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
output[prompt] = ''
|
314
|
-
end
|
300
|
+
if rm_prompt?(**opts) && (output[prompt])
|
301
|
+
case prompt
|
302
|
+
when String then output[prompt] = ""
|
303
|
+
when Regexp
|
304
|
+
if prompt.names.include?("prompt")
|
305
|
+
output[prompt, "prompt"] = ""
|
306
|
+
else
|
307
|
+
begin
|
308
|
+
output[prompt, 1] = ""
|
309
|
+
rescue IndexError
|
310
|
+
output[prompt] = ""
|
315
311
|
end
|
316
312
|
end
|
317
313
|
end
|
@@ -359,8 +355,8 @@ module Net
|
|
359
355
|
def net_ssh
|
360
356
|
return @net_ssh if @net_ssh
|
361
357
|
|
362
|
-
logger.debug {
|
363
|
-
self.net_ssh = Net::SSH.start(net_ssh_options[:ip] || net_ssh_options[:host] ||
|
358
|
+
logger.debug { "Net:SSH #start" }
|
359
|
+
self.net_ssh = Net::SSH.start(net_ssh_options[:ip] || net_ssh_options[:host] || "localhost", net_ssh_options[:user] || ENV["USER"], **formatted_net_ssh_options)
|
364
360
|
rescue StandardError => error
|
365
361
|
self.net_ssh = nil
|
366
362
|
raise
|
@@ -381,13 +377,13 @@ module Net
|
|
381
377
|
before_open_channel_procs.each { |_name, a_proc| instance_eval(&a_proc) }
|
382
378
|
::Timeout.timeout(open_channel_timeout, Error::OpenChannelTimeout) do
|
383
379
|
net_ssh.open_channel do |new_channel|
|
384
|
-
logger.debug
|
380
|
+
logger.debug "channel is open"
|
385
381
|
self.channel = new_channel
|
386
382
|
new_channel.request_pty(terminal_options) do |_ch, success|
|
387
383
|
raise Error::Pty, "#{host || ip} Failed to open ssh pty" unless success
|
388
384
|
end
|
389
|
-
new_channel.send_channel_request(
|
390
|
-
raise Error::RequestShell,
|
385
|
+
new_channel.send_channel_request("shell") do |_ch, success|
|
386
|
+
raise Error::RequestShell, "Failed to open ssh shell" unless success
|
391
387
|
end
|
392
388
|
new_channel.on_data do |_ch, data|
|
393
389
|
on_stdout(data)
|
@@ -395,9 +391,9 @@ module Net
|
|
395
391
|
# new_channel.on_extended_data do |_ch, type, data| end
|
396
392
|
# new_channel.on_close do end
|
397
393
|
end
|
398
|
-
until channel
|
394
|
+
process until channel
|
399
395
|
end
|
400
|
-
logger.debug
|
396
|
+
logger.debug "channel is ready, running callbacks now"
|
401
397
|
after_open_channel_procs.each { |_name, a_proc| instance_eval(&a_proc) }
|
402
398
|
process
|
403
399
|
self
|
@@ -415,7 +411,7 @@ module Net
|
|
415
411
|
end
|
416
412
|
|
417
413
|
def formatted_net_ssh_options
|
418
|
-
net_ssh_options.symbolize_keys.reject {|k,
|
414
|
+
net_ssh_options.symbolize_keys.reject { |k, _v| [:host, :ip, :user].include?(k) }
|
419
415
|
end
|
420
416
|
|
421
417
|
def rm_prompt?(**opts)
|
@@ -445,7 +441,7 @@ module Net
|
|
445
441
|
pixels_wide: terminal_pixels_width,
|
446
442
|
pixels_high: terminal_pixels_height,
|
447
443
|
modes: terminal_modes
|
448
|
-
}.reject {|
|
444
|
+
}.reject { |_k, v| v.nil? }
|
449
445
|
end
|
450
446
|
end
|
451
447
|
end
|
@@ -457,8 +453,9 @@ end
|
|
457
453
|
|
458
454
|
class Net::SSH::Connection::Session
|
459
455
|
attr_accessor :cli_channels
|
456
|
+
|
460
457
|
def cli(**opts)
|
461
|
-
cli_session = Net::SSH::CLI::Session.new({ net_ssh: self }.merge(opts))
|
458
|
+
cli_session = Net::SSH::CLI::Session.new(**{ net_ssh: self }.merge(opts))
|
462
459
|
cli_session.open_channel
|
463
460
|
cli_session
|
464
461
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: net-ssh-cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.9.
|
4
|
+
version: 1.9.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Fabian Stillhart
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-11-
|
11
|
+
date: 2022-11-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bump
|
@@ -140,7 +140,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
140
140
|
- !ruby/object:Gem::Version
|
141
141
|
version: '0'
|
142
142
|
requirements: []
|
143
|
-
rubygems_version: 3.3.
|
143
|
+
rubygems_version: 3.3.26
|
144
144
|
signing_key:
|
145
145
|
specification_version: 4
|
146
146
|
summary: 'Net::SSH::CLI: A library to handle CLI Sessions'
|