antimony-ssh 0.1.4

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
+ SHA1:
3
+ metadata.gz: e13880e85fe266cd7cff4b9eeb56076c71d0b0c3
4
+ data.tar.gz: 8f63105db29c061a17f537ba3c845d669f4b514d
5
+ SHA512:
6
+ metadata.gz: 9359b2f40479e62d65c1f814cc6e0b4921d60a455582f41cc3ae2ce93cda93e895c09cbcd5c8171422f637a4383cee015ada0d10d4b997ecb43cc35f7643a1a9
7
+ data.tar.gz: 85a45f33ed26cf25fbbf97bdcedfb748694c3364d8aee6fecdac6e139e00d08119db269e760449ed806857f250760545de689412f31366a12f53bc6ea55f864a
data/bin/sb ADDED
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ lib_path = File.expand_path('../../lib'.freeze, __FILE__)
5
+ ($LOAD_PATH.unshift lib_path) unless $LOAD_PATH.include? lib_path
6
+
7
+ require 'thor'
8
+ require 'logging'
9
+ require 'antimony'
10
+
11
+ class AntimonyCLI < Thor
12
+ desc 'formula FILE [--inputs my_input:hello other_input:goodbye]', 'run the specified formula'
13
+ method_option :inputs, type: :hash
14
+ def formula(file)
15
+ inputs = options[:inputs] || {}
16
+ formula = Antimony::Formula.new(file, inputs)
17
+ formula.run
18
+ end
19
+
20
+ default_task :formula
21
+ end
22
+
23
+ AntimonyCLI.start(ARGV)
@@ -0,0 +1,6 @@
1
+ # encoding: UTF-8
2
+ module Antimony
3
+ class Config
4
+ attr_accessor :show_output, :pretty
5
+ end
6
+ end
@@ -0,0 +1,63 @@
1
+ # encoding: UTF-8
2
+ module Antimony
3
+ class Formula
4
+ LOG_PARENT_PATH = Dir.pwd + '/log'
5
+ LOG_PATH = "#{LOG_PARENT_PATH}/formulas".freeze
6
+ FORMULAS_PATH = Dir.pwd + '/formulas'
7
+
8
+ attr_accessor :inputs, :outputs, :formula_log
9
+
10
+ def initialize(name, inputs = {})
11
+ ($LOAD_PATH.unshift FORMULAS_PATH) unless $LOAD_PATH.include? FORMULAS_PATH
12
+
13
+ @inputs = indifferent_hash(inputs)
14
+ @outputs = {}
15
+
16
+ init_log(name)
17
+
18
+ helper_path = "#{FORMULAS_PATH}/formula_helper.rb"
19
+
20
+ eval File.read(helper_path) if File.exist?(helper_path) # rubocop:disable Lint/Eval
21
+
22
+ @formula = File.read("#{FORMULAS_PATH}/#{name}.rb")
23
+ end
24
+
25
+ def run
26
+ eval(@formula) # rubocop:disable Lint/Eval
27
+ end
28
+
29
+ def session(host, username, password)
30
+ @connection = Antimony::Session.new(host, username, password)
31
+ yield if block_given?
32
+ @log.info @connection.session_log
33
+ @connection.close
34
+ @connection = nil
35
+ end
36
+
37
+ private
38
+
39
+ def init_log(name)
40
+ Dir.mkdir LOG_PARENT_PATH unless Dir.exist? LOG_PARENT_PATH
41
+ Dir.mkdir LOG_PATH unless Dir.exist? LOG_PATH
42
+ @log = Logging.logger[name]
43
+ layout = Logging.layouts.pattern(pattern: "%m\n")
44
+ @log.add_appenders(Logging.appenders.file("#{LOG_PATH}/#{name}.log", truncate: true,
45
+ layout: layout))
46
+ end
47
+
48
+ def method_missing(name, *args, &block)
49
+ raise 'No active connection!' unless @connection
50
+ @connection.send(name, *args, &block)
51
+ end
52
+
53
+ def indifferent_hash(hash)
54
+ {}.tap do |new_hash|
55
+ hash.each do |key, value|
56
+ value = indifferent_hash(value) if value.is_a?(Hash)
57
+ new_hash[key.to_sym] = value
58
+ new_hash[key.to_s] = value
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,132 @@
1
+ # encoding: UTF-8
2
+ module Antimony
3
+ class Session
4
+ def initialize(host, username, password)
5
+ @cursor_position = 0
6
+ @screen_text = blank_screen
7
+ @session_log = []
8
+
9
+ puts "Establishing SSH tunnel ..."
10
+ @gateway = Net::SSH::Gateway.new(host, username, :password => password)
11
+ tunnel_port = @gateway.open(host, 23)
12
+
13
+ puts "Connecting to #{host} using local port #{tunnel_port} ..."
14
+ @connection = Net::Telnet.new(
15
+ 'Host' => 'localhost',
16
+ 'Port' => tunnel_port,
17
+ 'Timeout' => 10
18
+ )
19
+
20
+ update_screen
21
+ end
22
+
23
+ def close
24
+ @connection.close
25
+ @gateway.shutdown!
26
+ end
27
+
28
+ def send_keys(keys, count = 1, text = true)
29
+ count.times { @connection.print keys }
30
+ update_screen
31
+ add_text(keys) if text
32
+ log_screen
33
+ print_screen if Antimony.configuration.show_output
34
+ end
35
+
36
+ def value_at(row, column, length)
37
+ start_index = ((row - 1) * 80) + (column - 1)
38
+ end_index = start_index + length - 1
39
+ @screen_text[start_index..end_index].join
40
+ end
41
+
42
+ def screen_text
43
+ @screen_text.join
44
+ end
45
+
46
+ def printable_screen_text
47
+ @screen_text.join.scan(LINE)
48
+ end
49
+
50
+ def print_screen
51
+ puts SCREEN_SEPARATOR
52
+ puts printable_screen_text
53
+ puts SCREEN_SEPARATOR
54
+ end
55
+
56
+ def session_log
57
+ txt = EMPTY.clone
58
+ @session_log.each_with_index do |entry, i|
59
+ txt += LOG_SEPARATOR + ENTER
60
+ txt += "#{i}: #{ENTER}"
61
+ txt += entry + ENTER
62
+ end
63
+ txt += LOG_SEPARATOR
64
+ txt
65
+ end
66
+
67
+ def log(text)
68
+ @session_log.push(text)
69
+ end
70
+
71
+ def log_screen
72
+ log(printable_screen_text.join("\n"))
73
+ end
74
+
75
+ private
76
+
77
+ def update_screen
78
+ @screen_data = receive_data
79
+ @screen_text = blank_screen if @screen_data.include?('[2J')
80
+ parse_ansi
81
+ end
82
+
83
+ def add_text(text)
84
+ text.chars.each do |char|
85
+ @screen_text[@cursor_position] = char
86
+ @cursor_position += 1
87
+ end
88
+ end
89
+
90
+ def receive_data
91
+ whole = []
92
+ whole.push(@chunk) while chunk
93
+ whole.join
94
+ end
95
+
96
+ def chunk
97
+ @chunk = @connection.waitfor RECEIVE_OPTS
98
+ rescue # rubocop:disable Lint/HandleExceptions
99
+ end
100
+
101
+ def cursor_position(esc)
102
+ esc_code = /\e\[/
103
+ pos = esc.gsub(esc_code, EMPTY).gsub(H, EMPTY).split(SEMICOLON)
104
+ row_index = pos[0].to_i - 1
105
+ col_index = pos[1].to_i - 1
106
+ (row_index * 80) + col_index
107
+ end
108
+
109
+ def parse_ansi
110
+ ansi = @screen_data.scan(ANSI_REGEX).map do |e|
111
+ {
112
+ value: e[0],
113
+ type: e[0].include?(ESCAPE) ? :esc : :chr
114
+ }
115
+ end
116
+
117
+ ansi.each do |e|
118
+ if (e[:type] == :esc) && (e[:value].end_with? H)
119
+ @cursor_position = cursor_position(e[:value])
120
+ elsif e[:type] == :chr
121
+ @screen_text[@cursor_position] = e[:value]
122
+ @cursor_position += 1
123
+ end
124
+ end
125
+ @screen_text
126
+ end
127
+
128
+ def blank_screen
129
+ (1..1920).to_a.map { |_i| SPACE }
130
+ end
131
+ end
132
+ end
data/lib/antimony.rb ADDED
@@ -0,0 +1,90 @@
1
+ # encoding: UTF-8
2
+ # encoding 'ASCII-8bit'
3
+ lib_path = File.expand_path('../antimony', __FILE__)
4
+ ($LOAD_PATH.unshift lib_path) unless $LOAD_PATH.include? lib_path
5
+
6
+ require 'bundler'
7
+ Bundler.require(:default)
8
+
9
+ # Misc
10
+ require 'net/telnet'
11
+ require 'net/ssh/gateway'
12
+ require 'logging'
13
+
14
+ # Gem
15
+ require "#{lib_path}/config.rb"
16
+ require "#{lib_path}/formula.rb"
17
+ require "#{lib_path}/session.rb"
18
+
19
+ module Antimony
20
+ class << self
21
+ attr_writer :configuration
22
+ end
23
+
24
+ def self.configuration
25
+ @configuration ||= Antimony::Config.new.tap { |config| config.show_output = false }
26
+ end
27
+
28
+ def self.configure
29
+ yield configuration if block_given?
30
+ end
31
+
32
+ # Constants
33
+ ANSI_REGEX = /(\e\[.{1,2};?.{0,2}m|\e\[.{1}J|\e\[.{1,2}A|\e\[.{1,2}B|\e\[.{1,2}C|\e\[.{1,2}D|\e\[K|\e\[.{1,2};.{1,2}H|\e\[.{3}|.)/ # rubocop:disable Metrics/LineLength
34
+ SCREEN_SEPARATOR = '################################################################################'.freeze
35
+ LOG_SEPARATOR = '================================================================================'.freeze
36
+
37
+ SPACE = ' '.freeze
38
+ EMPTY = ''.freeze
39
+ RECEIVE_OPTS = { 'Match' => /.{20}/, 'Timeout' => 1 }.freeze
40
+
41
+ ENTER = "\n".freeze
42
+ TAB = "\t".freeze
43
+ ESCAPE = "\e".freeze
44
+
45
+ H = 'H'.freeze
46
+
47
+ SEMICOLON = ';'.freeze
48
+
49
+ LINE = /.{80}/
50
+
51
+ class Session
52
+ KEYBOARD_METHODS = {
53
+ f1: "\x1B\x31".freeze,
54
+ f2: "\x1B\x32".freeze,
55
+ f3: "\x1B\x33".freeze,
56
+ f4: "\x1B\x34".freeze,
57
+ f5: "\x1B\x35".freeze,
58
+ f6: "\x1B\x36".freeze,
59
+ f7: "\x1B\x37".freeze,
60
+ f8: "\x1B\x38".freeze,
61
+ f9: "\x1B\x39".freeze,
62
+ f10: "\x1B\x30".freeze,
63
+ f11: "\x1B\x2D".freeze,
64
+ f12: "\x1B\x3D".freeze,
65
+ f13: "\x1B\x21".freeze,
66
+ f14: "\x1B\x40".freeze,
67
+ f15: "\x1B\x23".freeze,
68
+ f16: "\x1B\x24".freeze,
69
+ f17: "\x1B\x25".freeze,
70
+ f18: "\x1B\x5E".freeze,
71
+ f19: "\x1B\x26".freeze,
72
+ f20: "\x1B\x2A".freeze,
73
+ f21: "\x1B\x28".freeze,
74
+ f22: "\x1B\x29".freeze,
75
+ f23: "\x1B\x5F".freeze,
76
+ f24: "\x1B\x2B".freeze,
77
+ enter: "\n".freeze,
78
+ tab: "\t".freeze,
79
+ backspace: "\177".freeze,
80
+ arrow_up: "\e[A".freeze,
81
+ arrow_down: "\e[B".freeze,
82
+ arrow_right: "\e[C".freeze,
83
+ arrow_left: "\e[D".freeze
84
+ }.freeze
85
+
86
+ KEYBOARD_METHODS.each do |key, val|
87
+ define_method(key) { |count = 1| send_keys val, count, false }
88
+ end
89
+ end
90
+ end
metadata ADDED
@@ -0,0 +1,166 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: antimony-ssh
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.4
5
+ platform: ruby
6
+ authors:
7
+ - Andrew Terris
8
+ - Adrienne Hisbrook
9
+ - Lesley Dennison
10
+ - Ryan Rosenblum
11
+ - Vinh Luong
12
+ autorequire:
13
+ bindir: bin
14
+ cert_chain: []
15
+ date: 2020-09-29 00:00:00.000000000 Z
16
+ dependencies:
17
+ - !ruby/object:Gem::Dependency
18
+ requirement: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - "~>"
21
+ - !ruby/object:Gem::Version
22
+ version: '1.2'
23
+ name: logging
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: !ruby/object:Gem::Requirement
27
+ requirements:
28
+ - - "~>"
29
+ - !ruby/object:Gem::Version
30
+ version: '1.2'
31
+ - !ruby/object:Gem::Dependency
32
+ requirement: !ruby/object:Gem::Requirement
33
+ requirements:
34
+ - - "~>"
35
+ - !ruby/object:Gem::Version
36
+ version: '0'
37
+ name: thor
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - "~>"
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ - !ruby/object:Gem::Dependency
46
+ requirement: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - "~>"
49
+ - !ruby/object:Gem::Version
50
+ version: 4.2.0
51
+ name: net-ssh
52
+ type: :runtime
53
+ prerelease: false
54
+ version_requirements: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - "~>"
57
+ - !ruby/object:Gem::Version
58
+ version: 4.2.0
59
+ - !ruby/object:Gem::Dependency
60
+ requirement: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - "~>"
63
+ - !ruby/object:Gem::Version
64
+ version: 1.3.0
65
+ name: net-ssh-gateway
66
+ type: :runtime
67
+ prerelease: false
68
+ version_requirements: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - "~>"
71
+ - !ruby/object:Gem::Version
72
+ version: 1.3.0
73
+ - !ruby/object:Gem::Dependency
74
+ requirement: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - "~>"
77
+ - !ruby/object:Gem::Version
78
+ version: '10.3'
79
+ name: rake
80
+ type: :development
81
+ prerelease: false
82
+ version_requirements: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - "~>"
85
+ - !ruby/object:Gem::Version
86
+ version: '10.3'
87
+ - !ruby/object:Gem::Dependency
88
+ requirement: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - "~>"
91
+ - !ruby/object:Gem::Version
92
+ version: '3.0'
93
+ name: rspec
94
+ type: :development
95
+ prerelease: false
96
+ version_requirements: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - "~>"
99
+ - !ruby/object:Gem::Version
100
+ version: '3.0'
101
+ - !ruby/object:Gem::Dependency
102
+ requirement: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - "~>"
105
+ - !ruby/object:Gem::Version
106
+ version: 0.10.1
107
+ name: pry
108
+ type: :development
109
+ prerelease: false
110
+ version_requirements: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - "~>"
113
+ - !ruby/object:Gem::Version
114
+ version: 0.10.1
115
+ - !ruby/object:Gem::Dependency
116
+ requirement: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - "~>"
119
+ - !ruby/object:Gem::Version
120
+ version: 0.37.0
121
+ name: rubocop
122
+ type: :development
123
+ prerelease: false
124
+ version_requirements: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - "~>"
127
+ - !ruby/object:Gem::Version
128
+ version: 0.37.0
129
+ description: DSL for writing scripts to drive TN5250 green-screen applications using
130
+ Telnet via SSH tunneling
131
+ email: ''
132
+ executables:
133
+ - sb
134
+ extensions: []
135
+ extra_rdoc_files: []
136
+ files:
137
+ - bin/sb
138
+ - lib/antimony.rb
139
+ - lib/antimony/config.rb
140
+ - lib/antimony/formula.rb
141
+ - lib/antimony/session.rb
142
+ homepage: https://github.com/manheim/antimony
143
+ licenses:
144
+ - MIT
145
+ metadata: {}
146
+ post_install_message:
147
+ rdoc_options: []
148
+ require_paths:
149
+ - lib
150
+ required_ruby_version: !ruby/object:Gem::Requirement
151
+ requirements:
152
+ - - ">="
153
+ - !ruby/object:Gem::Version
154
+ version: '0'
155
+ required_rubygems_version: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ requirements: []
161
+ rubyforge_project:
162
+ rubygems_version: 2.6.14.1
163
+ signing_key:
164
+ specification_version: 4
165
+ summary: TN5250/AS400 automation
166
+ test_files: []