train-telnet 0.1.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 +7 -0
- data/Gemfile +10 -0
- data/README.md +48 -0
- data/lib/train-telnet.rb +7 -0
- data/lib/train-telnet/connection.rb +107 -0
- data/lib/train-telnet/transport.rb +30 -0
- data/lib/train-telnet/version.rb +5 -0
- data/train-telnet.gemspec +24 -0
- metadata +78 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ce78fe5b33ad84efb95d26d49d28e52696dba529df1cde3873334248e7b05e25
|
4
|
+
data.tar.gz: c5d5aff8f5b333c837c01bbc22ea1b1c8ccaed7441d8685616210bfdcaf8dd5f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a5a5093d1cb53a10dc80a307e936993dc4bf619062e7e119e8c76273b0d4e3a6ecf7a042440681723448b48c5cdae0daf08f9d5f5fc66b7957d2e2257a0b1022
|
7
|
+
data.tar.gz: b01e9efcffaf31bb8074ecd180490f7d2b6d78302aec23f647164000d46a8efdaaf7c29b49b87e3b272c75371d016c7a4f57678035520ff0dcdad55532298ddc
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# train-telnet - Train Plugin for connecting via Telnet
|
2
|
+
|
3
|
+
This plugin allows applications that rely on Train to communicate via Telnet.
|
4
|
+
|
5
|
+
Of course you already know that this is an insecure protocol - but if you look
|
6
|
+
for this plugin, you probably have some specific needs. Right? If not, please
|
7
|
+
try the default SSH transport included in Train.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
You will have to build this gem yourself to install it as it is not yet on
|
12
|
+
Rubygems.Org. For this there is a rake task which makes this a one-liner:
|
13
|
+
|
14
|
+
```bash
|
15
|
+
rake install:local
|
16
|
+
```
|
17
|
+
|
18
|
+
## Transport parameters
|
19
|
+
|
20
|
+
| Option | Explanation | Default |
|
21
|
+
| ----------------- | ---------------------------- | ---------------- |
|
22
|
+
| `host` | Hostname | (required) |
|
23
|
+
| `user` | Username to connect | (required) |
|
24
|
+
| `password` | Password to connect | (required) |
|
25
|
+
| `port` | Remote port | `23` |
|
26
|
+
| `login_prompt` | Username prompt on login | `Username: ` |
|
27
|
+
| `password_prompt` | Password prompt on login | `Password: ` |
|
28
|
+
| `setup` | Commands to issue on open | ` ` |
|
29
|
+
| `teardown` | Commands to issue on close | ` ` |
|
30
|
+
| `raw_output` | Suppress stdout processing | `false` |
|
31
|
+
| `error_pattern` | Regex to match error lines | `ERROR.*` |
|
32
|
+
| `prompt_pattern` | Regex to match device prompt | `[-a-zA-Z0-9]+(?:\((?:config\|config-[a-z]+\|vlan)\))?[#>]\s*$` |
|
33
|
+
|
34
|
+
## Example use
|
35
|
+
|
36
|
+
This will work for a Cisco IOS XE device with Telnet enabled:
|
37
|
+
```ruby
|
38
|
+
require "train"
|
39
|
+
train = Train.create("telnet", {
|
40
|
+
host: "10.0.0.1",
|
41
|
+
user: "username",
|
42
|
+
password: "password",
|
43
|
+
setup: "terminal length 0\n",
|
44
|
+
logger: Logger.new($stdout, level: :info)
|
45
|
+
})
|
46
|
+
conn = train.connection
|
47
|
+
result = conn.run_command("show version\n")
|
48
|
+
conn.close
|
data/lib/train-telnet.rb
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
require "net/telnet"
|
2
|
+
require "train"
|
3
|
+
|
4
|
+
module TrainPlugins
|
5
|
+
module Telnet
|
6
|
+
class Connection < Train::Plugins::Transport::BaseConnection
|
7
|
+
|
8
|
+
def initialize(options)
|
9
|
+
super(options)
|
10
|
+
end
|
11
|
+
|
12
|
+
def close
|
13
|
+
return if @session.nil?
|
14
|
+
|
15
|
+
unless @options[:teardown]&.empty?
|
16
|
+
logger.debug format("[Telnet] Sending teardown commands to %s:%d", @options[:host], @options[:port])
|
17
|
+
execute_on_channel(@options[:teardown])
|
18
|
+
end
|
19
|
+
|
20
|
+
logger.info format("[Telnet] Closed connection to %s:%d", @options[:host], @options[:port])
|
21
|
+
session.close
|
22
|
+
ensure
|
23
|
+
@session = nil
|
24
|
+
end
|
25
|
+
|
26
|
+
def uri
|
27
|
+
"telnet://#{options[:user]}@#{@options[:host]}:#{@options[:port]}/"
|
28
|
+
end
|
29
|
+
|
30
|
+
def run_command_via_connection(cmd, &data_handler)
|
31
|
+
logger.debug format("[Telnet] Sending command to %s:%d", @options[:host], @options[:port])
|
32
|
+
exit_status, stdout, stderr = execute_on_channel(cmd, &data_handler)
|
33
|
+
|
34
|
+
CommandResult.new(stdout, stderr, exit_status)
|
35
|
+
end
|
36
|
+
|
37
|
+
def execute_on_channel(cmd, &data_handler)
|
38
|
+
if @options[:debug_telnet]
|
39
|
+
logger.debug "[Telnet] => #{cmd}\n"
|
40
|
+
end
|
41
|
+
|
42
|
+
stdout = session.cmd(cmd)
|
43
|
+
stderr = ""
|
44
|
+
exit_status = 0
|
45
|
+
|
46
|
+
# Output needs to be flushed first, so we don't skip a beat (Check why)
|
47
|
+
session.waitfor("Waittime" => 0.1, "Match" => /./, "Timeout" => 0.1) rescue Net::ReadTimeout
|
48
|
+
|
49
|
+
# Remove \r in linebreaks
|
50
|
+
stdout.delete!("\r")
|
51
|
+
|
52
|
+
if @options[:debug_telnet]
|
53
|
+
logger.debug "[Telnet] <= '#{stdout}'"
|
54
|
+
end
|
55
|
+
|
56
|
+
# Extract command output only (no leading/trailing prompts)
|
57
|
+
unless @options[:raw_output]
|
58
|
+
stdout = stdout.match(/#{Regexp.quote(cmd.strip)}\n(.*?)\n#{@options[:prompt_pattern]}/m)&.captures&.first
|
59
|
+
end
|
60
|
+
stdout = "" if stdout.nil?
|
61
|
+
|
62
|
+
# Simulate exit code and stderr
|
63
|
+
errors = stdout.match(/^(#{@options[:error_pattern]})/)
|
64
|
+
if errors
|
65
|
+
exit_status = 1
|
66
|
+
stderr = errors.captures.first
|
67
|
+
stdout.gsub!(/^#{@options[:error_pattern]}/, "")
|
68
|
+
end
|
69
|
+
|
70
|
+
[exit_status, stdout, stderr]
|
71
|
+
end
|
72
|
+
|
73
|
+
def session(retry_options = {})
|
74
|
+
@session ||= create_session
|
75
|
+
end
|
76
|
+
|
77
|
+
def create_session
|
78
|
+
logger.info format("[Telnet] Opening connection to %s:%d", @options[:host], @options[:port])
|
79
|
+
|
80
|
+
@session = Net::Telnet.new(
|
81
|
+
"Host" => @options[:host],
|
82
|
+
"Port" => @options[:port],
|
83
|
+
"Prompt" => Regexp.new(@options[:prompt_pattern])
|
84
|
+
)
|
85
|
+
|
86
|
+
@session.login(
|
87
|
+
"Name" => @options[:user],
|
88
|
+
"Password" => @options[:password],
|
89
|
+
"LoginPrompt" => Regexp.new(@options[:login_prompt]),
|
90
|
+
"PasswordPrompt" => Regexp.new(@options[:password_prompt])
|
91
|
+
)
|
92
|
+
|
93
|
+
unless @options[:setup].empty?
|
94
|
+
logger.debug format("[Telnet] Sending setup commands to %s:%d", @options[:host], @options[:port])
|
95
|
+
|
96
|
+
execute_on_channel(@options[:setup])
|
97
|
+
end
|
98
|
+
|
99
|
+
@session
|
100
|
+
end
|
101
|
+
|
102
|
+
def reset_session
|
103
|
+
@session.close
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require "train-telnet/connection"
|
2
|
+
|
3
|
+
module TrainPlugins
|
4
|
+
module Telnet
|
5
|
+
class Transport < Train.plugin(1)
|
6
|
+
name "telnet"
|
7
|
+
|
8
|
+
option :host, required: true
|
9
|
+
option :user, required: true
|
10
|
+
option :password, required: true
|
11
|
+
|
12
|
+
option :port, default: 23
|
13
|
+
option :raw_output, default: false
|
14
|
+
|
15
|
+
option :login_prompt, default: "Username: "
|
16
|
+
option :password_prompt, default: "Password: "
|
17
|
+
option :setup, default: ""
|
18
|
+
option :teardown, default: ""
|
19
|
+
option :error_pattern, default: "ERROR: .*$"
|
20
|
+
option :prompt_pattern, default: "[-a-zA-Z0-9]+(?:\((?:config|config-[a-z]+|vlan)\))?[#>]\s*$"
|
21
|
+
|
22
|
+
# Non documented options for development
|
23
|
+
option :debug_telnet, default: false
|
24
|
+
|
25
|
+
def connection(_instance_opts = nil)
|
26
|
+
@connection ||= TrainPlugins::Telnet::Connection.new(@options)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
lib = File.expand_path("../lib", __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require "train-telnet/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "train-telnet"
|
7
|
+
spec.version = TrainPlugins::Telnet::VERSION
|
8
|
+
spec.authors = ["Thomas Heinen"]
|
9
|
+
spec.email = ["theinen@tecracer.de"]
|
10
|
+
spec.summary = "Train Transport for Telnet connections"
|
11
|
+
spec.description = "Allows applications using Train connect via Telnet"
|
12
|
+
spec.homepage = "https://github.com/tecracer_theinen/train-telnet"
|
13
|
+
spec.license = "Apache-2.0"
|
14
|
+
|
15
|
+
spec.files = %w{
|
16
|
+
README.md train-telnet.gemspec Gemfile
|
17
|
+
} + Dir.glob(
|
18
|
+
"lib/**/*", File::FNM_DOTMATCH
|
19
|
+
).reject { |f| File.directory?(f) }
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_dependency "train", "~> 2.0"
|
23
|
+
spec.add_dependency "net-telnet", "~> 0.2"
|
24
|
+
end
|
metadata
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: train-telnet
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Thomas Heinen
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-08-28 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: train
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: net-telnet
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.2'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.2'
|
41
|
+
description: Allows applications using Train connect via Telnet
|
42
|
+
email:
|
43
|
+
- theinen@tecracer.de
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- Gemfile
|
49
|
+
- README.md
|
50
|
+
- lib/train-telnet.rb
|
51
|
+
- lib/train-telnet/connection.rb
|
52
|
+
- lib/train-telnet/transport.rb
|
53
|
+
- lib/train-telnet/version.rb
|
54
|
+
- train-telnet.gemspec
|
55
|
+
homepage: https://github.com/tecracer_theinen/train-telnet
|
56
|
+
licenses:
|
57
|
+
- Apache-2.0
|
58
|
+
metadata: {}
|
59
|
+
post_install_message:
|
60
|
+
rdoc_options: []
|
61
|
+
require_paths:
|
62
|
+
- lib
|
63
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: '0'
|
73
|
+
requirements: []
|
74
|
+
rubygems_version: 3.0.3
|
75
|
+
signing_key:
|
76
|
+
specification_version: 4
|
77
|
+
summary: Train Transport for Telnet connections
|
78
|
+
test_files: []
|