xlogin 0.13.10 → 0.14.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 719819143a56b26ad14b31cbe6e61ed9d4df208c36dc9c060709bce170974d43
4
- data.tar.gz: e0d20e9e5cd5967cacb28a8d92bd0f342bd5ecf47e591e9b7b3fb6257e6d5690
3
+ metadata.gz: cdbb386674deee491268f35a9d5ca46c721391068d693a68d9a3e289e0168f97
4
+ data.tar.gz: b6afba886c65c3744253d58e0d8eccda3c265cf168159d95f3504408962127ce
5
5
  SHA512:
6
- metadata.gz: ce9b1a9bd127b38edd212875887773264019eb25a137eb4f84793b284671915246b2055d5e36c85a0d65d9350910262e642940f5f71d78400e6dffa8addaf4a8
7
- data.tar.gz: d57ea13b2a0fd0aee2e332cf941e4cf596253297695932944dda3a310932e50be72a9fd0ea099adce2c9784ac55da91c92c781d5b74d3238f418334796eb23f4
6
+ metadata.gz: edf8a747bff259718090617554b0d0868a3a3f321c287b328be882ac4dc68f9cbd2943755e55a1772525647bbfc485e849ac56495bed49a92b3a76cfb5bb4d1d
7
+ data.tar.gz: 5d699b1d3982c7cf1a7bd4cd19ef174acf27885ac5e38fc4f10f746efd99b8e1e3deea523f52b901f9d4cdb80440aae5dd532abde1d41aa09c7548e07198a838
@@ -22,7 +22,7 @@ module Xlogin
22
22
 
23
23
  class << self
24
24
  def list(*patterns)
25
- factory.list_inventory(*patterns)
25
+ factory.list_hostinfo(*patterns)
26
26
  end
27
27
 
28
28
  def find(*patterns)
@@ -34,7 +34,7 @@ module Xlogin
34
34
  when Hash then factory.build(**args.merge(**opts))
35
35
  when String then factory.build_from_hostname(args, **opts)
36
36
  else
37
- raise SessionError.new("Invalid argument: '#{args}'")
37
+ raise Xlogin::Error.new("Invalid argument: '#{args}'")
38
38
  end
39
39
 
40
40
  return session unless block
@@ -59,16 +59,16 @@ module Xlogin
59
59
  end
60
60
 
61
61
  def generate_templates(dir)
62
- FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
62
+ FileUtils.mkdir_p(dir)
63
63
  builtin_templates = Dir.glob(File.join(File.dirname(__FILE__), 'xlogin', 'templates', '*.rb'))
64
- builtin_templates.each { |file| FileUtils.cp(file, DEFAULT_TEMPLATE_DIR) }
64
+ builtin_templates.each{ |file| FileUtils.cp(file, DEFAULT_TEMPLATE_DIR) }
65
65
  end
66
66
 
67
- private
68
67
  def factory
69
68
  @factory ||= Xlogin::Factory.instance
70
69
  end
71
70
 
71
+ private
72
72
  def set(opts = {})
73
73
  @settings ||= {}
74
74
  opts.each do |key, val|
@@ -1,5 +1,3 @@
1
- #! /usr/bin/env ruby
2
-
3
1
  require 'optparse'
4
2
  require 'parallel'
5
3
  require 'stringio'
@@ -7,30 +5,32 @@ require 'stringio'
7
5
  module Xlogin
8
6
  class CLI
9
7
 
10
- DEFAULT_INVENTORY_FILE = File.join(ENV['HOME'], '.xloginrc')
11
- DEFAULT_TEMPLATE_DIR = File.join(ENV['HOME'], '.xlogin.d')
8
+ DEFAULT_INVENTORY = File.join(ENV['HOME'], '.xloginrc')
9
+ DEFAULT_TEMPLATE = File.join(ENV['HOME'], '.xlogin.d')
12
10
 
13
11
  def self.run(args = ARGV)
14
12
  Xlogin::CLI.new.run(args)
15
13
  end
16
14
 
17
15
  def list(config)
18
- puts Xlogin.list(*config[:pattern]).map { |e| e[:name] }.sort.uniq
16
+ puts Xlogin.list(*config[:patterns]).map{ |e| e[:name] }.sort.uniq
19
17
  end
20
18
 
21
19
  def tty(config)
22
- info = Xlogin.list(*config[:pattern]).shift
20
+ info = Xlogin.find(*config[:patterns])
23
21
  puts "Trying #{info[:name]}...", "Escape character is '^]'."
24
- session, _ = exec(config.merge(pattern: info[:name], jobs: 1))
22
+
23
+ session, _ = exec(config.merge(patterns: info[:name], jobs: 1))
25
24
  session.interact!
26
25
  end
27
26
 
28
27
  def exec(config)
29
- Signal.trap(:INT) { exit 0 }
28
+ Signal.trap(:INT){ exit 1 }
30
29
 
31
30
  jobs = config[:jobs] || 1
32
- hosts = Xlogin.list(*config[:pattern])
33
- width = hosts.map { |e| e[:name].length }.max
31
+ hosts = Xlogin.list(*config[:patterns])
32
+ width = hosts.map{ |e| e[:name].length }.max
33
+ raise "No host found: `#{config[:patterns].join(', ')}`" if hosts.empty?
34
34
 
35
35
  Parallel.map(hosts, in_threads: jobs) do |info|
36
36
  buffer = StringIO.new
@@ -40,17 +40,17 @@ module Xlogin
40
40
  begin
41
41
  loggers = []
42
42
  loggers << ((jobs > 1)? buffer : $stdout)
43
- loggers << File.expand_path(File.join(config[:"log-dir"], "#{info[:name]}.log"), ENV['PWD']) if config[:"log-dir"]
43
+ loggers << File.expand_path(File.join(config[:logdir], "#{info[:name]}.log"), ENV['PWD']) if config[:logdir]
44
44
 
45
45
  session = Xlogin.get(info.merge(log: loggers))
46
- session.enable(info[:enable]) if info[:enable] && Xlogin.settings.enable?
46
+ session.enable(session.config.enable) if session.config.enable && Xlogin.settings.enable?
47
47
 
48
- command_lines = ['', *config[:exec].to_s.split(';').map(&:strip)]
49
- command_lines.each { |line| session.cmd(line) }
48
+ command_lines = config[:command].flat_map { |e| e.to_s.split(';').map(&:strip) }
49
+ command_lines.each{ |line| session.cmd(line) }
50
50
 
51
- buffer.string.lines.each { |line| print prefix + line.gsub("\r", '') } if jobs > 1
51
+ buffer.string.lines.each{ |line| print prefix + line.gsub(/^.*\r/, '') } if jobs > 1
52
52
  rescue => e
53
- buffer.string.lines.each { |line| print prefix + line.gsub("\r", '') } if jobs > 1
53
+ buffer.string.lines.each{ |line| print prefix + line.gsub(/^.*\r/, '') } if jobs > 1
54
54
  raise e
55
55
  end
56
56
 
@@ -60,34 +60,38 @@ module Xlogin
60
60
 
61
61
  def run(args)
62
62
  config = Hash.new
63
- config[:env] = {}
63
+ config[:env] = []
64
+ config[:inventory] = []
65
+ config[:template] = []
66
+ config[:command] = []
64
67
  config[:runner] = self.method(:tty)
65
- config[:inventory] = DEFAULT_INVENTORY_FILE
66
- config[:template] = DEFAULT_TEMPLATE_DIR
67
68
 
68
69
  parser = OptionParser.new
69
70
  parser.banner = "#{File.basename($0)} HOST_PATTERN [Options]"
70
71
  parser.version = Xlogin::VERSION
71
72
 
72
- parser.on('-i PATH', '--inventory', String, 'The PATH to the inventory file.')
73
- parser.on('-t PATH', '--template', String, 'The PATH to the template file or directory.')
74
- parser.on('-L PATH', '--log-dir', String, 'The PATH to the log directory.') { |v| v || '.' }
73
+ parser.on('-i PATH', '--inventory', String, 'The PATH to the inventory file.') { |v| config[:inventory] << v }
74
+ parser.on('-t PATH', '--template', String, 'The PATH to the template file or directory.'){ |v| config[:template] << v }
75
+ parser.on('-L PATH', '--log-dir', String, 'The PATH to the log directory.') { |v| config[:logdir] = v }
75
76
 
76
- parser.on('-l', '--list', TrueClass, 'List the inventory.') { |v| config[:runner] = self.method(:list) }
77
- parser.on('-e COMMAND', '--exec', String, 'Execute commands and quit.') { |v| config[:runner] = self.method(:exec); v }
77
+ parser.on('-l', '--list', TrueClass, 'List the inventory.') { |v| config[:runner] = self.method(:list) }
78
+ parser.on('-e COMMAND', '--exec', String, 'Execute commands and quit.'){ |v| config[:runner] = self.method(:exec); config[:command] << v }
78
79
 
79
- parser.on('-E KEY=VAL', '--env', /(\w+=\w+)+/, 'Environment variables.')
80
- parser.on('-j NUM', '--jobs', Integer, 'The NUM of jobs to execute in parallel.')
80
+ parser.on('-E KEY=VAL', '--env', /(\w+=\w+)+/, 'Environment variables.') { |v| config[:env] << v }
81
+ parser.on('-j NUM', '--jobs', Integer, 'The NUM of jobs to execute in parallel.'){ |v| config[:jobs] = v }
81
82
 
82
- config[:pattern] = parser.parse!(args, into: config)
83
+ config[:patterns] = parser.parse!(args)
84
+ config[:inventory] << DEFAULT_INVENTORY if config[:inventory].empty?
85
+ config[:template] << DEFAULT_TEMPLATE if config[:template].empty?
83
86
 
84
87
  Xlogin.configure do
85
- set Hash[config[:env].map{ |v| v.split('=') }]
86
- source File.expand_path(config[:inventory], ENV['PWD'])
87
- template File.expand_path(config[:template], ENV['PWD'])
88
+ set Hash[config[:env].map{ |v| v.split('=') }]
89
+
90
+ source *config[:inventory].map{ |e| File.expand_path(e, ENV['PWD']) }
91
+ template *config[:template].map { |e| File.expand_path(e, ENV['PWD']) }
88
92
  end
89
- raise "No host found: `#{args.join(', ')}`" if Xlogin.list(*config[:pattern]).empty?
90
93
 
94
+ raise Xlogin::Error.new("Invalid host pattern: '#{config[:patterns].join(' ')}'") if Xlogin.list(*config[:patterns]).empty?
91
95
  config[:runner].call(config)
92
96
  rescue => e
93
97
  $stderr.puts e, '', parser
@@ -1,5 +1,7 @@
1
1
  require 'addressable/uri'
2
+ require 'net/ssh/gateway'
2
3
  require 'singleton'
4
+ require 'thread'
3
5
  require 'xlogin/session_pool'
4
6
  require 'xlogin/template'
5
7
 
@@ -9,30 +11,32 @@ module Xlogin
9
11
  include Singleton
10
12
 
11
13
  def initialize
12
- @inventory = Hash.new
13
- @templates = Hash.new
14
+ @inventory = Hash.new
15
+ @templates = Hash.new
16
+ @gateways = Hash.new
17
+ @mutex = Mutex.new
14
18
  end
15
19
 
16
- def set_inventory(name, **opts)
17
- @inventory[name] = (get_inventory(name) || {name: name}).merge(opts)
20
+ def set_hostinfo(name, **opts)
21
+ @inventory[name] = (get_hostinfo(name) || {name: name}).merge(opts)
18
22
  end
19
23
 
20
- def get_inventory(name)
24
+ def get_hostinfo(name)
21
25
  @inventory[name]
22
26
  end
23
27
 
24
- def list_inventory(*patterns)
28
+ def list_hostinfo(*patterns)
25
29
  return @inventory.values if patterns.empty?
26
30
 
27
- values1 = patterns.map do |pattern|
31
+ values1 = patterns.compact.map do |pattern|
28
32
  values2 = pattern.split(',').map do |entry|
29
33
  key, val = entry.to_s.split(':')
30
34
  key, val = 'name', key if val.nil?
31
- @inventory.values.select { |e| File.fnmatch(val, e[key.to_sym]) }
35
+ @inventory.values.select{ |e| File.fnmatch(val, e[key.to_sym]) }
32
36
  end
33
- values2.reduce(&:&)
37
+ values2.reduce(&:&) || []
34
38
  end
35
- values1.reduce(&:|)
39
+ values1.reduce(&:|) || []
36
40
  end
37
41
 
38
42
  def set_template(name, text = nil, &block)
@@ -50,6 +54,35 @@ module Xlogin
50
54
  @templates.keys
51
55
  end
52
56
 
57
+ def open_tunnel(tunnel, host, port)
58
+ @mutex.synchronize do
59
+ unless @gateways[tunnel]
60
+ gateway_uri = Addressable::URI.parse(tunnel)
61
+ case gateway_uri.scheme
62
+ when 'ssh'
63
+ username, password = *gateway_uri.userinfo.split(':')
64
+ @gateways[tunnel] = Net::SSH::Gateway.new(
65
+ gateway_uri.host,
66
+ username,
67
+ password: password,
68
+ port: gateway_uri.port || 22
69
+ )
70
+ end
71
+ end
72
+
73
+ gateway = @gateways[tunnel]
74
+ return host, port unless gateway
75
+ return '127.0.0.1', gateway.open(host, port)
76
+ end
77
+ end
78
+
79
+ def close_tunnel(tunnel, port)
80
+ @mutex.synchronize do
81
+ gateway = @gateways[tunnel]
82
+ gateway.close(port) if gateway
83
+ end
84
+ end
85
+
53
86
  def build(type:, **opts)
54
87
  template = get_template(type)
55
88
  raise Xlogin::Error.new("Template not found: '#{type}'") unless template
@@ -62,31 +95,25 @@ module Xlogin
62
95
  end
63
96
 
64
97
  def build_from_hostname(args, **opts)
65
- hostinfo = get_inventory(args)
98
+ hostinfo = get_hostinfo(args)
66
99
  raise Xlogin::Error.new("Host not found: '#{args}'") unless hostinfo
67
100
 
68
- build(hostinfo.merge(name: args, **opts))
101
+ build(**hostinfo.merge(**opts))
69
102
  end
70
103
 
71
104
  def method_missing(method_name, *args, **opts, &block)
72
- super unless args.size == 2 && %r{^\S+://\S+} =~ args[1]
105
+ super unless args.size == 2 && Addressable::URI::URIREGEX =~ args[1]
73
106
 
74
- type = method_name.to_s.downcase
75
107
  name = args[0]
76
108
  uri = args[1]
77
- set_inventory(name, type: type, uri: uri, **opts)
109
+ type = method_name.to_s.downcase
110
+ set_hostinfo(name.to_s, type: type, uri: uri, **opts)
78
111
  end
79
112
 
80
113
  private
81
114
  def uri(**opts)
82
115
  return Addressable::URI.parse(opts[:uri].strip) if opts.key?(:uri)
83
-
84
- scheme = opts[:scheme].strip
85
- address = opts.values_at(:host, :port).compact.map(&:strip).join(':')
86
- userinfo = opts[:userinfo].strip
87
- userinfo ||= opts.values_at(:username, :password).compact.map(&:strip).join(':')
88
-
89
- Addressable::URI.parse("#{scheme}://" + [userinfo, address].compact.join('@'))
116
+ Addressable::URI.new(**opts)
90
117
  rescue
91
118
  raise Xlogin::Error.new("Invalid target - '#{opts}'")
92
119
  end
@@ -1,7 +1,6 @@
1
1
  require 'time'
2
2
  require 'rake'
3
3
  require 'rake/tasklib'
4
- require 'ostruct'
5
4
  require 'stringio'
6
5
  require 'colorize'
7
6
 
@@ -13,11 +12,11 @@ module Xlogin
13
12
 
14
13
  def generate(*patterns, **opts, &block)
15
14
  description = Rake.application.last_description
16
- hostnames = Xlogin.list(*patterns).map { |e| e[:name] }
15
+ hostnames = Xlogin.list(*patterns).map{ |e| e[:name] }
17
16
 
18
17
  task 'all' => hostnames unless opts[:all] == false
19
18
  hostnames.each do |hostname|
20
- desc "#{description} @#{hostname}" if opts[:all] == false
19
+ desc "#{description} with #{hostname}"
21
20
  RakeTask.new(hostname, &block)
22
21
  end
23
22
  end
@@ -32,8 +31,8 @@ module Xlogin
32
31
  end
33
32
 
34
33
  attr_reader :name
35
- attr_accessor :lock
36
34
  attr_accessor :log
35
+ attr_accessor :lock
37
36
  attr_accessor :timeout
38
37
  attr_accessor :silent
39
38
  attr_accessor :fail_on_error
@@ -49,8 +48,8 @@ module Xlogin
49
48
  define
50
49
  end
51
50
 
52
- def name_with_scope
53
- [*Rake.application.current_scope.to_a.reverse, name].join(':')
51
+ def name_with_scope(separator = ':')
52
+ [*Rake.application.current_scope.to_a.reverse, name].join(separator)
54
53
  end
55
54
 
56
55
  def run(&block)
@@ -78,47 +77,41 @@ module Xlogin
78
77
  end
79
78
 
80
79
  def run_task
81
- buffer = StringIO.new
82
-
83
- logger = log ? [log] : []
84
- logger.push buffer
85
- logger.push $stdout if !silent && !Rake.application.options.always_multitask
80
+ buffer = StringIO.new
81
+ loggers = []
82
+ loggers << log if log
83
+ loggers << $stdout if !silent && !Rake.application.options.always_multitask
84
+ loggers << buffer
86
85
 
87
- session = Xlogin.get(name, log: logger, timeout: timeout)
86
+ session = Xlogin.get(name, log: loggers, timeout: timeout)
88
87
 
89
88
  @runner.call(session)
90
- $stdout.print format_log(buffer.string) if !silent && Rake.application.options.always_multitask
89
+ $stdout.print log_text(buffer.string) if !silent && Rake.application.options.always_multitask
91
90
 
92
91
  return true
93
92
  rescue => e
94
93
  RakeTask.shutdown! if fail_on_error
95
94
 
96
- buffer.puts("\n[ERROR] #{e}".colorize(color: :white, background: :red))
97
- $stderr.print "\n" + format_log(buffer.string.chomp).colorize(color: :light_red)
98
- $stderr.print "\n"
95
+ session.comment(e.to_s, prefix: "[ERROR]", chomp: true, color: :white, background: :red)
96
+ $stderr.print log_text(buffer.string + "\n").colorize(color: :light_red) if Rake.application.options.always_multitask
99
97
 
100
98
  return false
101
99
  ensure
102
100
  session.close rescue nil
103
101
  end
104
102
 
105
- def format_log(text)
106
- text.lines.map do |line|
107
- "#{Time.now.iso8601} - #{name}\t|#{line.gsub(/^\s*[\r]+/, '')}"
108
- end.join
103
+ def log_text(text)
104
+ text.lines.map{ |line| "#{Time.now.iso8601} - #{name}\t|#{line.gsub(/^.*\r/, '')}" }.join
109
105
  end
110
106
 
111
107
  end
112
108
 
113
109
  module SessionModule
114
- def msg(text, prefix: "[INFO]", chomp: false, **color)
115
- default_color = { color: :green }
116
110
 
117
- log("\n")
118
- log(Time.now.iso8601.colorize(**color) + ' ') if !Rake.application.options.always_multitask
119
-
120
- log("#{prefix} #{text}".colorize(**default_color.merge(color)))
111
+ def comment(line, prefix: "[INFO]", chomp: false, **color)
112
+ write_log("#{prefix} #{line}".colorize({color: :light_white}.merge(**color)))
121
113
  cmd('') unless chomp
122
114
  end
115
+
123
116
  end
124
117
  end
@@ -1,14 +1,11 @@
1
- require 'addressable/uri'
2
- require 'delegate'
3
1
  require 'fileutils'
4
- require 'net/ssh/gateway'
5
- require 'ostruct'
6
2
  require 'stringio'
7
3
  require 'thread'
8
4
 
9
5
  module Xlogin
10
6
  module SessionModule
11
7
 
8
+ attr_reader :config
12
9
  attr_accessor :name
13
10
 
14
11
  def initialize(template, uri, **opts)
@@ -21,16 +18,14 @@ module Xlogin
21
18
  end
22
19
 
23
20
  @name = opts[:name] || @host
24
- @config = OpenStruct.new(opts)
21
+ @tunnel = opts[:tunnel] || opts[:via]
22
+ @config = ReadOnlyStruct.new(opts)
25
23
  @template = template
26
- @username, @password = uri.userinfo.to_s.split(':')
24
+ @loggers = [@config.log].flatten.uniq.reduce({}){ |a, e| a.merge(e => build_log(e)) }
25
+ @host, @port = Xlogin.factory.open_tunnel(@tunnel, @host, @port) if @tunnel
27
26
 
28
- forward = @config.forward || @config.via
29
- ssh_tunnel(forward) if forward
30
27
  max_retry = @config.retry || 1
31
-
32
- @mutex = Mutex.new
33
- @loggers = [@config.log].flatten.uniq.reduce({}) { |a, e| a.merge(e => build_logger(e)) }
28
+ username, password = uri.userinfo.to_s.split(':')
34
29
 
35
30
  begin
36
31
  args = Hash.new
@@ -42,8 +37,8 @@ module Xlogin
42
37
  else
43
38
  args['Host' ] = @host
44
39
  args['Port' ] = @port
45
- args['Username'] = @username
46
- args['Password'] = @password
40
+ args['Username'] = username
41
+ args['Password'] = password
47
42
  end
48
43
 
49
44
  super(args)
@@ -63,47 +58,54 @@ module Xlogin
63
58
  end
64
59
 
65
60
  def prompt_pattern
66
- Regexp.union(*@template.prompt.map(&:first))
61
+ Regexp.union(*@template.prompts.map(&:first))
67
62
  end
68
63
 
69
64
  def duplicate(type: @template.name, **args)
70
65
  template = Xlogin::Factory.instance.get_template(type)
66
+ raise Xlogin::Error.new("Template not found: '#{type}'") unless template
67
+
71
68
  template.build(@uri, **@config.to_h.merge(args))
72
69
  end
73
70
 
74
- def puts(*args, &block)
75
- args = args.flat_map { |arg| instance_exec(arg, &@template.interrupt!) }.compact if @template.interrupt!
76
- args.empty? ? super('', &block) : super(*args, &block)
71
+ def puts(string = '', &block)
72
+ super(string, &block)
77
73
  end
78
74
 
79
- def waitfor(*args, &block)
80
- args = [prompt_pattern] if args.empty?
81
- @mutex.synchronize { _waitfor(*args, &block) }
75
+ def print(string = '', &block)
76
+ string = instance_exec(string, &@template.interrupt!) if @template.interrupt!
77
+ super(string, &block)
82
78
  end
83
79
 
84
- def close
85
- logout if respond_to?(:logout)
86
- @mutex.synchronize do
87
- @loggers.each do |_, logger|
88
- next if logger.nil? || [$stdout, $stderr].include?(logger)
89
- logger.close
90
- end
80
+ def waitfor(*args, &block)
81
+ return waitfor(prompt_pattern) if args.empty?
91
82
 
92
- if @gateway
93
- @gateway.close(@port)
94
- @gateway.shutdown!
95
- end
83
+ line = super(*args) do |recv|
84
+ write_log(recv)
85
+ block.call(recv) if block
86
+ end
96
87
 
97
- super
88
+ _, process = @template.prompts.find{ |r, p| r =~ line && p }
89
+ if process
90
+ instance_eval(&process)
91
+ line += waitfor(*args, &block)
98
92
  end
93
+
94
+ return line
99
95
  end
100
96
 
101
- def log(text)
102
- @loggers.each { |_, logger| logger.syswrite(text) if logger }
97
+ def close
98
+ @loggers.each do |_, logger|
99
+ next if [$stdout, $stderr, nil].include?(logger)
100
+ logger.close
101
+ end
102
+
103
+ Xlogin.factory.close_tunnel(@tunnel, @port) if @tunnel
104
+ super
103
105
  end
104
106
 
105
107
  def enable_log(log = $stdout)
106
- @loggers.update(log => build_logger(log))
108
+ @loggers.update(log => build_log(log))
107
109
  if block_given?
108
110
  yield
109
111
  disable_log(log)
@@ -119,40 +121,11 @@ module Xlogin
119
121
  end
120
122
 
121
123
  private
122
- def _waitfor(*args, &block)
123
- __waitfor = method(:waitfor).super_method
124
- line = __waitfor.call(*args) do |recv|
125
- log(recv)
126
- block.call(recv) if block
127
- end
128
-
129
- _, process = @template.prompt.find { |r, p| r =~ line && p }
130
- if process
131
- instance_eval(&process)
132
- line += _waitfor(*args, &block)
133
- end
134
-
135
- return line
136
- end
137
-
138
- def ssh_tunnel(gateway)
139
- gateway_uri = Addressable::URI.parse(gateway)
140
- case gateway_uri.scheme
141
- when 'ssh'
142
- username, password = *gateway_uri.userinfo.split(':')
143
- @gateway = Net::SSH::Gateway.new(
144
- gateway_uri.host,
145
- username,
146
- password: password,
147
- port: gateway_uri.port || 22
148
- )
149
-
150
- @port = @gateway.open(@host, @port)
151
- @host = '127.0.0.1'
152
- end
124
+ def write_log(text)
125
+ @loggers.each{ |_, logger| logger.syswrite(text) if logger }
153
126
  end
154
127
 
155
- def build_logger(log)
128
+ def build_log(log)
156
129
  case log
157
130
  when String
158
131
  FileUtils.mkdir_p(File.dirname(log))
@@ -22,23 +22,23 @@ module Xlogin
22
22
  end
23
23
 
24
24
  def size=(val)
25
- @mutex.synchronize { @size = val }
25
+ @mutex.synchronize{ @size = val }
26
26
  end
27
27
 
28
28
  def idle=(val)
29
- @mutex.synchronize { @idle = val }
29
+ @mutex.synchronize{ @idle = val }
30
30
  end
31
31
 
32
32
  def with
33
33
  session = deq
34
- Thread.handle_interrupt(Exception => :immediate) { yield session }
34
+ Thread.handle_interrupt(Exception => :immediate){ yield session }
35
35
  ensure
36
36
  enq session
37
37
  end
38
38
 
39
39
  def close
40
- while @queue.empty?
41
- session, _, _ = @queue.deq
40
+ until @queue.empty?
41
+ session, _ = @queue.deq
42
42
  destroy(session)
43
43
  end
44
44
  end
@@ -58,10 +58,10 @@ module Xlogin
58
58
  end
59
59
 
60
60
  begin
61
- raise IOError if session.sock.closed?
61
+ raise IOError if session&.sock&.closed?
62
62
  rescue IOError, EOFError, Errno::ECONNABORTED, Errno::ECONNREFUSED, Errno::ECONNRESET
63
63
  destroy(session)
64
- session = deq
64
+ return deq
65
65
  end
66
66
 
67
67
  session
@@ -2,7 +2,7 @@ module Xlogin
2
2
  class ExpectationError < StandardError
3
3
 
4
4
  def initialize(expect, actual)
5
- super("Expected to match #{expect}")
5
+ super(expect)
6
6
  @actual = actual
7
7
  end
8
8
 
@@ -14,28 +14,22 @@ module Xlogin
14
14
 
15
15
  class Expectation
16
16
 
17
- def initialize(session, *args)
18
- @session = session
19
- @args = args
17
+ def initialize(result)
18
+ @result = result
20
19
  end
21
20
 
22
21
  def to_match(regexp)
23
- return if match(regexp)
24
- raise ExpectationError.new(@expect, @actual)
25
- end
22
+ regexp = Regexp.new(regexp.to_s) unless regexp.kind_of?(Regexp)
23
+ return if @result =~ regexp
26
24
 
27
- def not_to_match(regexp)
28
- return unless match(regexp)
29
- raise ExpectationError.new(@expect, @actual)
25
+ raise ExpectationError.new("Expected to match #{regexp}", @result)
30
26
  end
31
27
 
32
- private
33
- def match(regexp)
34
- regexp = Regexp.new(regexp.to_s) unless regexp.kind_of?(Regexp)
35
- @expect = regexp.inspect
28
+ def not_to_match(regexp)
29
+ regexp = Regexp.new(regexp.to_s) unless regexp.kind_of?(Regexp)
30
+ return if @result !~ regexp
36
31
 
37
- @actual ||= @session.cmd(*@args)
38
- @actual =~ regexp
32
+ raise ExpectationError.new("Expected not to match #{regexp}", @result)
39
33
  end
40
34
 
41
35
  end
@@ -43,9 +37,8 @@ module Xlogin
43
37
  module SessionModule
44
38
 
45
39
  def expect(*args)
46
- Expectation.new(self, *args)
40
+ Expectation.new(cmd(*args))
47
41
  end
48
- alias_method :exp, :expect
49
42
 
50
43
  end
51
44
  end
@@ -38,7 +38,7 @@ module Xlogin
38
38
  @sock.syswrite(bs)
39
39
  when @sock
40
40
  begin
41
- log(fh.readpartial(1024))
41
+ write_log(fh.readpartial(1024))
42
42
  rescue Errno::EAGAIN
43
43
  retry
44
44
  end
@@ -47,7 +47,7 @@ module Xlogin
47
47
  end
48
48
  rescue EOFError, Errno::ECONNRESET
49
49
  $stdout.puts "\r\n", "Conneciton closed.", "\r\n"
50
- self.close
50
+ close
51
51
  ensure
52
52
  $stdin.cooked!
53
53
  end
@@ -8,54 +8,44 @@ module Xlogin
8
8
  DEFAULT_PROMPT = /[$%#>] ?\z/n
9
9
  RESERVED_METHODS = %i( login logout enable disable )
10
10
 
11
- attr_reader :name, :scopes, :methods
11
+ attr_reader :name, :methods
12
12
 
13
13
  def initialize(name)
14
14
  @name = name
15
- @scopes = Hash.new
16
- @timeout = DEFAULT_TIMEOUT
17
15
  @prompts = Array.new
18
16
  @methods = Hash.new
17
+ @timeout = DEFAULT_TIMEOUT
19
18
  @interrupt = nil
20
19
  end
21
20
 
22
- def timeout(val = nil)
23
- @timeout = val.to_i if val
24
- @timeout
21
+ def prompt(expect, &block)
22
+ @prompts << [Regexp.new(expect.to_s), block]
25
23
  end
26
24
 
27
- def prompt(expect = nil, &block)
28
- return [[DEFAULT_PROMPT, nil]] if expect.nil? && @prompts.empty?
29
- @prompts << [Regexp.new(expect.to_s), block] if expect
25
+ def prompts
26
+ @prompts << [DEFAULT_PROMPT, nil] if @prompts.empty?
30
27
  @prompts
31
28
  end
32
29
 
33
- def scope(name = nil, &block)
34
- @scopes[name] = block
30
+ def bind(name, &block)
31
+ @methods[name] = block
35
32
  end
36
33
 
37
- def bind(name = nil, &block)
38
- @methods[name] = block
34
+ def timeout(val = nil)
35
+ @timeout = val.to_i if val
36
+ @timeout
39
37
  end
40
38
 
41
39
  def interrupt!(&block)
42
- return @interrupt unless block
43
- @interrupt = block
40
+ @interrupt = block if block
41
+ @interrupt
44
42
  end
45
43
 
46
44
  def build(uri, **opts)
47
45
  klass = Class.new(Xlogin.const_get(uri.scheme.capitalize))
48
46
  klass.class_exec(self) do |template|
49
- scopes = [*opts[:scope]].compact
50
- scopes.each { |scope| template.instance_eval(&template.scopes[scope]) }
51
-
52
47
  template.methods.each do |name, block|
53
- case name.to_s
54
- when 'enable'
55
- define_method(name) { |args = nil| instance_exec(args || opts[:enable], &block) }
56
- else
57
- define_method(name, &block)
58
- end
48
+ define_method(name, &block)
59
49
  end
60
50
  end
61
51
 
@@ -64,7 +54,7 @@ module Xlogin
64
54
 
65
55
  def method_missing(name, *, &block)
66
56
  super unless RESERVED_METHODS.include?(name)
67
- bind(name) { |*args| instance_exec(*args, &block) }
57
+ bind(name){ |*args| instance_exec(*args, &block) }
68
58
  end
69
59
 
70
60
  end
@@ -1,3 +1,3 @@
1
1
  module Xlogin
2
- VERSION = "0.13.10"
2
+ VERSION = "0.14.4"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: xlogin
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.10
4
+ version: 0.14.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - haccht
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-04-20 00:00:00.000000000 Z
11
+ date: 2020-06-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: net-telnet