train-telnet 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,10 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ group :development do
6
+ gem "pry"
7
+ gem "bundler"
8
+ gem "rake"
9
+ gem "chefstyle"
10
+ end
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
@@ -0,0 +1,7 @@
1
+ libdir = File.dirname(__FILE__)
2
+ $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
3
+
4
+ require "train-telnet/version"
5
+
6
+ require "train-telnet/transport"
7
+ require "train-telnet/connection"
@@ -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,5 @@
1
+ module TrainPlugins
2
+ module Telnet
3
+ VERSION = "0.1.0".freeze
4
+ end
5
+ 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: []