train-core 2.0.8 → 2.0.12
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/train/transports/cisco_ios_connection.rb +128 -0
- data/lib/train/version.rb +1 -1
- metadata +4 -31
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6428f10f1fcb461fff9d6a262ed8a8c643ca0aaf21cc60a0ddeff1573f4e3c0e
|
4
|
+
data.tar.gz: a1d8450b00960078fb2116ec40dd3f1e61a5976a4aa6aa14e4c0a3e73596f27b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 338cd5ee7abda4182c71c3132336b647ab5353d90934673fdf156577506167e109e557b6c139288b5dcc89476742a34eb692b95bfd99ad7159775f531f90c3ee
|
7
|
+
data.tar.gz: d15361a0bc4362272ea783c5198dea4ef6dbb5beb1a3878e04eb4484126f0b5daa2fdb0dbe725b5828da9cf48519f3d43730860a5a4e7de3ceaac7284b13f765
|
@@ -0,0 +1,128 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class Train::Transports::SSH
|
4
|
+
class CiscoIOSConnection < BaseConnection
|
5
|
+
class BadEnablePassword < Train::TransportError; end
|
6
|
+
|
7
|
+
def initialize(options)
|
8
|
+
super(options)
|
9
|
+
|
10
|
+
# Extract options to avoid passing them in to `Net::SSH.start` later
|
11
|
+
@host = options.delete(:host)
|
12
|
+
@user = options.delete(:user)
|
13
|
+
@port = options.delete(:port)
|
14
|
+
@enable_password = options.delete(:enable_password)
|
15
|
+
|
16
|
+
# Use all options left that are not `nil` for `Net::SSH.start` later
|
17
|
+
@ssh_options = options.reject { |_key, value| value.nil? }
|
18
|
+
|
19
|
+
@prompt = /^\S+[>#]\r\n.*$/
|
20
|
+
end
|
21
|
+
|
22
|
+
def uri
|
23
|
+
"ssh://#{@user}@#{@host}:#{@port}"
|
24
|
+
end
|
25
|
+
|
26
|
+
def unique_identifier
|
27
|
+
result = run_command_via_connection('show version | include Processor')
|
28
|
+
result.stdout.split(' ')[-1]
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def establish_connection
|
34
|
+
logger.debug("[SSH] opening connection to #{self}")
|
35
|
+
|
36
|
+
Net::SSH.start(@host, @user, @ssh_options)
|
37
|
+
end
|
38
|
+
|
39
|
+
def session
|
40
|
+
return @session unless @session.nil?
|
41
|
+
|
42
|
+
@session = open_channel(establish_connection)
|
43
|
+
|
44
|
+
# Escalate privilege to enable mode if password is given
|
45
|
+
if @enable_password
|
46
|
+
# This verifies we are not in privileged exec mode before running the
|
47
|
+
# enable command. Otherwise, the password will be in history.
|
48
|
+
if run_command_via_connection('show privilege').stdout.split[-1] != '15'
|
49
|
+
run_command_via_connection("enable\r\n#{@enable_password}")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Prevent `--MORE--` by removing terminal length limit
|
54
|
+
run_command_via_connection('terminal length 0')
|
55
|
+
|
56
|
+
@session
|
57
|
+
end
|
58
|
+
|
59
|
+
def run_command_via_connection(cmd, &_data_handler)
|
60
|
+
# Ensure buffer is empty before sending data
|
61
|
+
@buf = ''
|
62
|
+
|
63
|
+
logger.debug("[SSH] Running `#{cmd}` on #{self}")
|
64
|
+
session.send_data(cmd + "\r\n")
|
65
|
+
|
66
|
+
logger.debug('[SSH] waiting for prompt')
|
67
|
+
until @buf =~ @prompt
|
68
|
+
raise BadEnablePassword if @buf =~ /Bad secrets/
|
69
|
+
session.connection.process(0)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Save the buffer and clear it for the next command
|
73
|
+
output = @buf.dup
|
74
|
+
@buf = ''
|
75
|
+
|
76
|
+
format_result(format_output(output, cmd))
|
77
|
+
end
|
78
|
+
|
79
|
+
ERROR_MATCHERS = [
|
80
|
+
'Bad IP address',
|
81
|
+
'Incomplete command',
|
82
|
+
'Invalid input detected',
|
83
|
+
'Unrecognized host',
|
84
|
+
].freeze
|
85
|
+
|
86
|
+
# IOS commands do not have an exit code so we must compare the command
|
87
|
+
# output with partial segments of known errors. Then, we return a
|
88
|
+
# `CommandResult` with arguments in the correct position based on the
|
89
|
+
# result.
|
90
|
+
def format_result(result)
|
91
|
+
if ERROR_MATCHERS.none? { |e| result.include?(e) }
|
92
|
+
CommandResult.new(result, '', 0)
|
93
|
+
else
|
94
|
+
CommandResult.new('', result, 1)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# The buffer (@buf) contains all data sent/received on the SSH channel so
|
99
|
+
# we need to format the data to match what we would expect from Train
|
100
|
+
def format_output(output, cmd)
|
101
|
+
leading_prompt = /(\r\n|^)\S+[>#]/
|
102
|
+
command_string = /#{Regexp.quote(cmd)}\r\n/
|
103
|
+
trailing_prompt = /\S+[>#](\r\n|$)/
|
104
|
+
trailing_line_endings = /(\r\n)+$/
|
105
|
+
|
106
|
+
output
|
107
|
+
.sub(leading_prompt, '')
|
108
|
+
.sub(command_string, '')
|
109
|
+
.gsub(trailing_prompt, '')
|
110
|
+
.gsub(trailing_line_endings, '')
|
111
|
+
end
|
112
|
+
|
113
|
+
# Create an SSH channel that writes to @buf when data is received
|
114
|
+
def open_channel(ssh)
|
115
|
+
logger.debug("[SSH] opening SSH channel to #{self}")
|
116
|
+
ssh.open_channel do |ch|
|
117
|
+
ch.on_data do |_, data|
|
118
|
+
@buf += data
|
119
|
+
end
|
120
|
+
|
121
|
+
ch.send_channel_request('shell') do |_, success|
|
122
|
+
raise 'Failed to open SSH shell' unless success
|
123
|
+
logger.debug('[SSH] shell opened')
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
data/lib/train/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: train-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.12
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dominik Richter
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-04-
|
11
|
+
date: 2019-04-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json
|
@@ -90,34 +90,6 @@ dependencies:
|
|
90
90
|
- - "<"
|
91
91
|
- !ruby/object:Gem::Version
|
92
92
|
version: '3.0'
|
93
|
-
- !ruby/object:Gem::Dependency
|
94
|
-
name: ed25519
|
95
|
-
requirement: !ruby/object:Gem::Requirement
|
96
|
-
requirements:
|
97
|
-
- - "~>"
|
98
|
-
- !ruby/object:Gem::Version
|
99
|
-
version: '1.2'
|
100
|
-
type: :runtime
|
101
|
-
prerelease: false
|
102
|
-
version_requirements: !ruby/object:Gem::Requirement
|
103
|
-
requirements:
|
104
|
-
- - "~>"
|
105
|
-
- !ruby/object:Gem::Version
|
106
|
-
version: '1.2'
|
107
|
-
- !ruby/object:Gem::Dependency
|
108
|
-
name: bcrypt_pbkdf
|
109
|
-
requirement: !ruby/object:Gem::Requirement
|
110
|
-
requirements:
|
111
|
-
- - "~>"
|
112
|
-
- !ruby/object:Gem::Version
|
113
|
-
version: '1.0'
|
114
|
-
type: :runtime
|
115
|
-
prerelease: false
|
116
|
-
version_requirements: !ruby/object:Gem::Requirement
|
117
|
-
requirements:
|
118
|
-
- - "~>"
|
119
|
-
- !ruby/object:Gem::Version
|
120
|
-
version: '1.0'
|
121
93
|
- !ruby/object:Gem::Dependency
|
122
94
|
name: winrm
|
123
95
|
requirement: !ruby/object:Gem::Requirement
|
@@ -187,6 +159,7 @@ files:
|
|
187
159
|
- lib/train/plugins.rb
|
188
160
|
- lib/train/plugins/base_connection.rb
|
189
161
|
- lib/train/plugins/transport.rb
|
162
|
+
- lib/train/transports/cisco_ios_connection.rb
|
190
163
|
- lib/train/transports/local.rb
|
191
164
|
- lib/train/transports/mock.rb
|
192
165
|
- lib/train/transports/ssh.rb
|
@@ -206,7 +179,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
206
179
|
requirements:
|
207
180
|
- - ">="
|
208
181
|
- !ruby/object:Gem::Version
|
209
|
-
version: '
|
182
|
+
version: '2.4'
|
210
183
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
211
184
|
requirements:
|
212
185
|
- - ">="
|