xlogin 0.8.3 → 0.8.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
  SHA1:
3
- metadata.gz: 1d9127e12b562fc160b59f27eb380c61c80d91cf
4
- data.tar.gz: bddfb491a1dc4d53b5b61464aa550cfaa45024a9
3
+ metadata.gz: d0c4789d65178ce5f60077b6d9f1f066872a7e8f
4
+ data.tar.gz: d41b0cdf51f5a566c84757104cb5094e1504ba1b
5
5
  SHA512:
6
- metadata.gz: 016bbc7159b7f0afae1c0f162d55111723290f7700a8cbf179a0176b6f1a321e220bf7a0ccb020e85fa65a7c666444792c51c414e1e850ef2d9528a9ff1c1e63
7
- data.tar.gz: d2446df25335c0a95188efdf5ecd6a441ed3d00979af7a5d0aed1b6c587ce4318e39de92b2dd86b0f2445abef5eec2b4334f84b0c1680d4af397849fb8bb36a7
6
+ metadata.gz: b9b3b1cf81d0f967cfaa2cfb3d630f1e1e1ede4abd3c5671b3b85eec429a5f5f6b088cf0fe763a61f6e8b1c6c977baa0b227f522465d38028347099fb47f3ca5
7
+ data.tar.gz: 3005ee45108fc3c5f61b0c2abf0c77916db5a0561508274a9e9c4498d4c3dc7cc529136d3cf35f95a82ab6152f3c9c131772e952dc04ed85d5c74dfb5e6f355e
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- xlogin (0.8.1)
4
+ xlogin (0.8.4)
5
5
  net-ssh
6
6
  net-ssh-gateway
7
7
  net-ssh-telnet
data/lib/xlogin/cli.rb CHANGED
@@ -4,6 +4,7 @@ require 'optparse'
4
4
  require 'ostruct'
5
5
  require 'parallel'
6
6
  require 'readline'
7
+ require 'socket'
7
8
  require 'stringio'
8
9
 
9
10
  module Xlogin
@@ -20,13 +21,15 @@ module Xlogin
20
21
 
21
22
  def self.getopts(args)
22
23
  config = OpenStruct.new(
23
- parallel: 1,
24
+ jobs: 1,
25
+ port: 8080,
26
+ taskname: :tty,
24
27
  inventory: nil,
25
28
  templates: [],
26
29
  )
27
30
 
28
31
  parser = OptionParser.new
29
- parser.banner = "#{File.basename($0)} HOST [TASK ARGUMENTS] [Options]"
32
+ parser.banner = "#{File.basename($0)} HOST_PATTERN [Options]"
30
33
  parser.version = Xlogin::VERSION
31
34
 
32
35
  parser.on('-i PATH', '--inventory', String, 'The PATH to the inventory file(default: $HOME/.xloginrc).') { |v| config.inventory = v }
@@ -34,14 +37,20 @@ module Xlogin
34
37
  parser.on('-T DIRECTORY', '--template-dir', String, 'The DIRECTORY of the template files.') { |v| config.templates += Dir.glob(File.join(v, '*.rb')) }
35
38
  parser.on('-L [DIRECTORY]', '--log-dir', String, 'The DIRECTORY of the log files(default: $PWD).') { |v| config.logdir = v || '.' }
36
39
 
37
- parser.on('-j NUM', '--jobs', Integer, 'The NUM of jobs to execute in parallel(default: 1).') { |v| config.parallel = v }
38
- parser.on('-e', '--enable', TrueClass, 'Try to gain enable priviledge.') { |v| config.enable = v }
40
+ parser.on('-l', '--list', TrueClass, 'List all available devices.') { |v| config.taskname = :list }
41
+ parser.on('-e', '--exec', TrueClass, 'Execute commands and quit.') { |v| config.taskname = :exec }
42
+ parser.on('-t', '--tty', TrueClass, 'Allocate a pseudo-tty.') { |v| config.taskname = :tty }
43
+
44
+ parser.on('-p NUM', '--port', Integer, 'Run as server on specified port(default: 8080).') { |v| config.taskname = :listen; config.port = v }
45
+ parser.on('-j NUM', '--jobs', Integer, 'The NUM of jobs to execute in parallel(default: 1).') { |v| config.jobs = v }
46
+
47
+ parser.on('-E', '--enable', TrueClass, 'Try to gain enable priviledge.') { |v| config.enable = v }
39
48
  parser.on('-y', '--assume-yes', TrueClass, 'Automatically answer yes to prompts.') { |v| config.authorize = v }
40
49
 
41
50
  begin
42
51
  args = parser.parse!(args)
43
- hostlist = args.shift
44
- taskname = args.shift || 'tty'
52
+ host = args.shift
53
+ config.args = args
45
54
 
46
55
  Xlogin.configure do
47
56
  config.inventory ||= DEFAULT_INVENTORY_FILE
@@ -55,13 +64,8 @@ module Xlogin
55
64
  load_templates(*config.templates.map { |file| File.expand_path(file, ENV['PWD']) })
56
65
  end
57
66
 
58
- config.hostlist = hostlist.to_s.split(',').flat_map { |pattern| Xlogin.factory.list(pattern) }
59
- config.taskname = taskname.to_s.downcase.tr('-', '_')
60
- config.arguments = args
61
-
62
- methods = Xlogin::CLI.instance_methods(false)
63
- raise "No host found: `#{hostlist}`" if config.hostlist.empty?
64
- raise "No task defined: `#{taskname}`" if config.taskname.empty? || methods.find_index(config.taskname.to_sym).nil?
67
+ config.hostlist = host.to_s.split(',').flat_map { |pattern| Xlogin.factory.list(pattern) }
68
+ raise "No host found: `#{host}`" if config.hostlist.empty?
65
69
  rescue => e
66
70
  $stderr.puts e, '', parser
67
71
  exit 1
@@ -71,55 +75,98 @@ module Xlogin
71
75
  end
72
76
 
73
77
  def list(config)
74
- width = config.hostlist.map { |e| e[:name].length }.max
75
- $stdout.puts config.hostlist.map { |e| "#{e[:name].to_s.ljust(width)} #{e[:type]}" }.sort
78
+ wid1 = config.hostlist.map { |e| e[:name].length }.max
79
+ wid2 = config.hostlist.map { |e| e[:type].length }.max
80
+ list = config.hostlist.map { |e| "#{e[:name].to_s.ljust(wid1)} #{e[:type].to_s.ljust(wid2)} #{e[:uri]}" }.sort
81
+ $stdout.puts list
76
82
  end
77
83
 
78
84
  def tty(config)
79
85
  Signal.trap(:INT) { exit 0 }
86
+
80
87
  list = config.hostlist.sort_by { |e| e[:name] }
81
88
  list.each do |target|
82
- Readline.readline(">> #{target[:name]} ", false) unless list.size == 1
83
- $stdout.puts "Trying #{target[:name]}...", "Escape character is '^]'."
89
+ unless list.size == 1
90
+ case resp = Readline.readline(">> #{target[:name]}(Y/n)? ", false).strip
91
+ when /^y(es)?$/i, ''
92
+ when /^n(o)?$/i then next
93
+ else redo
94
+ end
95
+ end
96
+
97
+ config.jobs = 1
84
98
  config.hostlist = [target]
85
- login(config) { |session| session.interact! }
99
+
100
+ $stdout.puts "Trying #{target[:name]}...", "Escape character is '^]'."
101
+ session, _ = exec(config)
102
+ session.interact!
86
103
  end
87
104
  end
88
105
 
89
- def exec(config)
90
- command_lines = ['', *config.arguments.flat_map { |e| e.split(';') }].map(&:strip)
91
- login(config) { |session| command_lines.each { |command| session.cmd(command) } }
106
+ def listen(config)
107
+ Signal.trap(:INT) { exit 0 }
108
+ config.jobs = config.hostlist.size
109
+
110
+ width = config.hostlist.map { |e| e[:name].length }.max
111
+ sessions = exec(config).compact
112
+
113
+ $stdout.puts "", ""
114
+ $stdout.puts "=> Start xlogin server on port=#{config.port}"
115
+ $stdout.puts "=> Ctrl-C to shutdown"
116
+
117
+ server = TCPServer.open(config.port)
118
+ socket = server.accept
119
+ while line = socket.gets
120
+ Parallel.each(sessions, in_threads: sessions.size) do |session|
121
+ resp = session.cmd(line.chomp)
122
+ prefix = "#{session.name.to_s.ljust(width)} |"
123
+ output = resp.to_s.lines.map { |line| prefix + line.chomp.gsub("\r", '') + "\n" }.join
124
+ socket.print output
125
+ $stdout.print output if config.jobs > 1
126
+ end
127
+ end
128
+ ensure
129
+ socket.close if socket
130
+ server.close if server
92
131
  end
93
132
 
94
- def load(config)
95
- command_lines = ['', *config.arguments.flat_map { |e| IO.readlines(e) }].map(&:strip)
96
- login(config) { |session| command_lines.each { |command| session.cmd(command) } }
97
- end
133
+ def exec(config, &block)
134
+ width = config.hostlist.map { |e| e[:name].length }.max
135
+
136
+ Parallel.map(config.hostlist, in_threads: config.jobs) do |hostinfo|
137
+ session = nil
138
+ error = nil
98
139
 
99
- private
100
- def login(config, &block)
101
- Parallel.map(config.hostlist, in_threads: config.parallel) do |hostinfo|
102
140
  begin
103
141
  buffer = StringIO.new
104
142
  hostname = hostinfo[:name]
105
143
 
106
144
  loggers = []
107
- loggers << ((config.parallel > 1)? buffer : $stdout)
145
+ loggers << ((config.jobs > 1)? buffer : $stdout)
108
146
  loggers << File.expand_path(File.join(config.logdir, "#{hostname}.log"), ENV['PWD']) if config.logdir
109
147
 
110
148
  session = Xlogin.get(hostinfo.merge(log: loggers))
111
149
  session.enable if config.enable && hostinfo[:enable]
112
150
 
113
- block.call(session)
151
+ command_lines = ['', *config.args.flat_map { |e| e.split(';') }].map(&:strip)
152
+ command_lines.each { |line| session.cmd(line) }
153
+
154
+ block.call(session) if block
114
155
  rescue => e
115
- lines = (config.parallel > 1)? ["\n#{hostname}\t| [Error] #{e}"] : ["\n[Error] #{e}"]
116
- lines.each { |line| $stderr.print "#{line.chomp}\n" }
117
- end
156
+ error = e
157
+ ensure
158
+ if config.jobs > 1
159
+ prefix = "#{hostname.to_s.ljust(width)} |"
160
+ output = buffer.string.lines.map { |line| prefix + line.chomp.gsub("\r", '') + "\n" }.join
161
+ $stdout.print output
162
+ $stderr.print prefix + "[Error] #{error}\n" if error
163
+ else
164
+ $stderr.print "[Error] #{error}\n" if error
165
+ end
118
166
 
119
- if config.parallel > 1
120
- lines = buffer.string.lines.map { |line| "#{hostname}\t| " + line.gsub("\r", '') }
121
- lines.each { |line| $stdout.print "#{line.chomp}\n" }
122
167
  end
168
+
169
+ session
123
170
  end
124
171
  end
125
172
 
@@ -1,3 +1,3 @@
1
1
  module Xlogin
2
- VERSION = "0.8.3"
2
+ VERSION = "0.8.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.8.3
4
+ version: 0.8.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - haccht
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-05-10 00:00:00.000000000 Z
11
+ date: 2018-05-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: net-telnet