antimony-ssh 0.1.4
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/bin/sb +23 -0
- data/lib/antimony/config.rb +6 -0
- data/lib/antimony/formula.rb +63 -0
- data/lib/antimony/session.rb +132 -0
- data/lib/antimony.rb +90 -0
- metadata +166 -0
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,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: []
|