xlogin 0.12.11 → 0.13.1
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 +4 -4
- data/lib/xlogin.rb +44 -43
- data/lib/xlogin/cli.rb +60 -83
- data/lib/xlogin/delegator.rb +1 -1
- data/lib/xlogin/factory.rb +5 -7
- data/lib/xlogin/version.rb +1 -1
- data/xlogin.gemspec +1 -0
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f6a3054375bed554e54f7d09096df9a5d0bf6e1e614db1b90ddec6c6091ee184
|
4
|
+
data.tar.gz: d5a2f79a8c1a2e233c267af0ba7c2b469125f88a3e011467251c8198d8990310
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '04915fd0733db1d164a83fab63745aef560c75a3d1d5739765f2933af75a3f3cd336e35e99ad589a51a9f5bc15d738e651878c36737e3598f40150db6caefef3'
|
7
|
+
data.tar.gz: 2739edd7f265bd8fc12a50ca04fca9bcd80788a75ba54484907c887e1c2b60e068b9b9f2e44f2b31f54566398ba3274b107781b888db90748752b29e2e3ee0e3
|
data/lib/xlogin.rb
CHANGED
@@ -1,20 +1,21 @@
|
|
1
1
|
$:.unshift File.dirname(__FILE__)
|
2
2
|
|
3
3
|
require 'net/http'
|
4
|
+
require 'ostruct'
|
4
5
|
require 'xlogin/factory'
|
5
6
|
require 'xlogin/version'
|
6
7
|
|
7
8
|
module Xlogin
|
8
9
|
|
9
|
-
class
|
10
|
-
class
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
def factory
|
15
|
-
@factory ||= Xlogin::Factory.instance
|
10
|
+
class Error < StandardError; end
|
11
|
+
class Settings < OpenStruct
|
12
|
+
def initialize(*args, &block)
|
13
|
+
super(*args, &block)
|
14
|
+
freeze
|
16
15
|
end
|
16
|
+
end
|
17
17
|
|
18
|
+
class << self
|
18
19
|
def list(*patterns)
|
19
20
|
factory.list_inventory(*patterns)
|
20
21
|
end
|
@@ -27,12 +28,14 @@ module Xlogin
|
|
27
28
|
session = case args
|
28
29
|
when Hash then factory.build(**args.merge(**opts))
|
29
30
|
when String then factory.build_from_hostname(args, **opts)
|
30
|
-
else
|
31
|
+
else
|
32
|
+
raise SessionError.new("Invalid argument: '#{args}'")
|
31
33
|
end
|
32
34
|
|
33
35
|
return session unless block
|
34
36
|
begin block.call(session) ensure session.close end
|
35
37
|
end
|
38
|
+
alias_method :create, :get
|
36
39
|
|
37
40
|
def get_pool(args, **opts, &block)
|
38
41
|
pool = factory.build_pool(args, **opts)
|
@@ -40,13 +43,15 @@ module Xlogin
|
|
40
43
|
return pool unless block
|
41
44
|
block.call(pool)
|
42
45
|
end
|
46
|
+
alias_method :create_pool, :get_pool
|
43
47
|
|
44
48
|
def configure(&block)
|
45
49
|
instance_eval(&block)
|
46
50
|
end
|
47
51
|
|
48
|
-
def
|
49
|
-
@
|
52
|
+
def settings
|
53
|
+
@settings ||= {}
|
54
|
+
Settings.new(@settings)
|
50
55
|
end
|
51
56
|
|
52
57
|
def generate_templates(dir)
|
@@ -56,54 +61,50 @@ module Xlogin
|
|
56
61
|
end
|
57
62
|
|
58
63
|
private
|
59
|
-
def
|
60
|
-
@
|
64
|
+
def factory
|
65
|
+
@factory ||= Xlogin::Factory.instance
|
61
66
|
end
|
62
67
|
|
63
|
-
def
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
end
|
68
|
+
def set(**opts)
|
69
|
+
@settings ||= {}
|
70
|
+
opts.each do |key, val|
|
71
|
+
val = val.call if val.kind_of?(Proc)
|
72
|
+
@settings.update(key.to_sym => val)
|
69
73
|
end
|
74
|
+
end
|
70
75
|
|
71
|
-
|
76
|
+
def source(*sources, &block)
|
77
|
+
return factory.instance_eval(&block) if block
|
78
|
+
|
79
|
+
sources.each do |path|
|
80
|
+
raise Xlogin::Error.new("Inventory file not found: #{path}") unless File.exist?(path)
|
81
|
+
factory.instance_eval(IO.read(path), path)
|
82
|
+
end
|
72
83
|
end
|
73
84
|
|
74
85
|
def template(*templates, **opts, &block)
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
+
return factory.set_template(templates.shift, &block) if block && templates.size == 1
|
87
|
+
|
88
|
+
templates.each do |template|
|
89
|
+
return template_url(template, **opts) if template =~ URI.regexp(['http', 'https'])
|
90
|
+
raise Xlogin::Error.new("Template file or directory not found: #{template}") unless File.exist?(template)
|
91
|
+
|
92
|
+
paths = [template] if File.file?(template)
|
93
|
+
paths = Dir.glob(File.join(template, '*.rb')) if File.directory?(template)
|
94
|
+
paths.each do |path|
|
95
|
+
name = opts[:type] || File.basename(path, '.rb').scan(/\w+/).join('_')
|
96
|
+
factory.set_template(name, IO.read(path))
|
86
97
|
end
|
87
98
|
end
|
88
|
-
|
89
|
-
name = opts[:type] || templates.first
|
90
|
-
raise ArgumentError.new('Missing template name') unless name
|
91
|
-
factory.set_template(name, &block)
|
92
99
|
end
|
93
100
|
|
94
101
|
def template_url(*template_urls, **opts)
|
95
|
-
template_urls.
|
96
|
-
uri
|
102
|
+
template_urls.each do |url|
|
103
|
+
uri = URI(url.to_s)
|
97
104
|
name = opts[:type] || File.basename(uri.path, '.rb').scan(/\w+/).join('_')
|
98
|
-
|
99
|
-
if text =~ /\w+.rb$/
|
100
|
-
uri.path = File.join(File.dirname(uri.path), text.lines.first.chomp)
|
101
|
-
text = Net::HTTP.get(uri)
|
102
|
-
end
|
103
|
-
factory.set_template(name, text)
|
105
|
+
factory.set_template(name, Net::HTTP.get(uri))
|
104
106
|
end
|
105
107
|
end
|
106
|
-
|
107
108
|
end
|
108
109
|
|
109
110
|
end
|
data/lib/xlogin/cli.rb
CHANGED
@@ -1,9 +1,7 @@
|
|
1
1
|
#! /usr/bin/env ruby
|
2
2
|
|
3
3
|
require 'optparse'
|
4
|
-
require 'ostruct'
|
5
4
|
require 'parallel'
|
6
|
-
require 'readline'
|
7
5
|
require 'stringio'
|
8
6
|
|
9
7
|
module Xlogin
|
@@ -13,112 +11,91 @@ module Xlogin
|
|
13
11
|
DEFAULT_TEMPLATE_DIR = File.join(ENV['HOME'], '.xlogin.d')
|
14
12
|
|
15
13
|
def self.run(args = ARGV)
|
16
|
-
|
17
|
-
client = Xlogin::CLI.new
|
18
|
-
client.method(config.task.first).call(config)
|
19
|
-
end
|
20
|
-
|
21
|
-
def self.getopts(args)
|
22
|
-
config = OpenStruct.new(
|
23
|
-
jobs: 1,
|
24
|
-
task: [:tty, nil],
|
25
|
-
assume_yes: false,
|
26
|
-
inventory: DEFAULT_INVENTORY_FILE,
|
27
|
-
template_dir: DEFAULT_TEMPLATE_DIR,
|
28
|
-
)
|
29
|
-
|
30
|
-
parser = OptionParser.new
|
31
|
-
parser.banner = "#{File.basename($0)} HOST_PATTERN [Options]"
|
32
|
-
parser.version = Xlogin::VERSION
|
33
|
-
|
34
|
-
parser.on('-i PATH', '--inventory', String, 'The PATH to the inventory file (default: $HOME/.xloginrc).') { |v| config.inventory = v }
|
35
|
-
parser.on('-T PATH', '--template', String, 'The PATH to the template dir (default: $HOME/.xlogin.d).') { |v| config.template_dir = v }
|
36
|
-
parser.on('-L [DIRECTORY]', '--log-dir', String, 'The PATH to the log dir (default: $PWD).') { |v| config.logdir = v || '.' }
|
37
|
-
|
38
|
-
parser.on('-l', '--list', TrueClass, 'List the inventory.') { |v| config.task = [:list, nil] }
|
39
|
-
parser.on('-t', '--tty', TrueClass, 'Allocate a pseudo-tty.') { |v| config.task = [:tty, nil] }
|
40
|
-
parser.on('-e COMMAND', '--exec', 'Execute commands and quit.') { |v| config.task = [:exec, v] }
|
41
|
-
|
42
|
-
parser.on('-j NUM', '--jobs', Integer, 'The NUM of jobs to execute in parallel(default: 1).') { |v| config.jobs = v }
|
43
|
-
parser.on('-E', '--enable', TrueClass, 'Try to gain enable priviledge.') { |v| config.enable = v }
|
44
|
-
parser.on('-y', '--assume-yes', TrueClass, 'Automatically answer yes to prompts.') { |v| config.assume_yes = v }
|
45
|
-
|
46
|
-
parser.parse!(args)
|
47
|
-
Xlogin.configure do
|
48
|
-
assume_yes(true)
|
49
|
-
source(File.expand_path(config.inventory, ENV['PWD']))
|
50
|
-
template(File.expand_path(config.template_dir, ENV['PWD']))
|
51
|
-
end
|
52
|
-
|
53
|
-
config.hosts = Xlogin.list(*args)
|
54
|
-
raise "No host found: `#{args.join(', ')}`" if config.hosts.empty?
|
55
|
-
|
56
|
-
return config
|
57
|
-
rescue => e
|
58
|
-
$stderr.puts e, '', parser
|
59
|
-
exit 1
|
14
|
+
Xlogin::CLI.new.run(args)
|
60
15
|
end
|
61
16
|
|
62
17
|
def list(config)
|
63
|
-
$stdout.puts config.
|
18
|
+
$stdout.puts Xlogin.list(*config[:pattern]).map { |e| e[:name] }.sort.uniq
|
64
19
|
end
|
65
20
|
|
66
21
|
def tty(config)
|
67
|
-
|
68
|
-
|
69
|
-
config.hosts.each do |hostinfo|
|
70
|
-
unless config.hosts.size == 1
|
71
|
-
case resp = Readline.readline(">> #{hostinfo[:name]}(Y/n)? ", false).strip
|
72
|
-
when /^y(es)?$/i, ''
|
73
|
-
when /^n(o)?$/i then next
|
74
|
-
else redo
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
$stdout.puts "Trying #{hostinfo[:name]}...", "Escape character is '^]'."
|
79
|
-
tty_config = OpenStruct.new(config.to_h.merge(jobs: 1, hosts: [hostinfo]))
|
22
|
+
info = Xlogin.list(*config[:pattern]).first
|
23
|
+
$stdout.puts "Trying #{info[:name]}...", "Escape character is '^]'."
|
80
24
|
|
81
|
-
|
82
|
-
|
83
|
-
end
|
25
|
+
session, _ = exec(config.merge(jobs: 1))
|
26
|
+
session.interact!
|
84
27
|
end
|
85
28
|
|
86
29
|
def exec(config)
|
87
30
|
Signal.trap(:INT) { exit 0 }
|
88
31
|
|
89
|
-
|
90
|
-
|
32
|
+
jobs = config[:jobs] || 1
|
33
|
+
hosts = Xlogin.list(*config[:pattern])
|
34
|
+
width = hosts.map { |e| e[:name].length }.max
|
35
|
+
|
36
|
+
Parallel.map(hosts, in_threads: jobs) do |info|
|
37
|
+
buffer = StringIO.new
|
38
|
+
prefix = "#{info[:name].to_s.ljust(width)} |"
|
91
39
|
session = nil
|
92
|
-
error = nil
|
93
40
|
|
94
41
|
begin
|
95
|
-
buffer = StringIO.new
|
96
|
-
|
97
42
|
loggers = []
|
98
|
-
loggers << ((
|
99
|
-
loggers << File.expand_path(File.join(config
|
43
|
+
loggers << ((jobs > 1)? buffer : $stdout)
|
44
|
+
loggers << File.expand_path(File.join(config[:log_dir], "#{info[:name]}.log"), ENV['PWD']) if config[:log_dir]
|
100
45
|
|
101
|
-
session = Xlogin.get(
|
102
|
-
session.enable(
|
46
|
+
session = Xlogin.get(info.merge(log: loggers))
|
47
|
+
session.enable(info[:enable]) if config[:enable] && info[:enable]
|
103
48
|
|
104
|
-
command_lines = ['', *config.
|
49
|
+
command_lines = ['', *config[:exec].to_s.split(';').map(&:strip)]
|
105
50
|
command_lines.each { |line| session.cmd(line) }
|
106
|
-
rescue => err
|
107
|
-
error = err
|
108
|
-
end
|
109
51
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
$stderr.print prefix + "[Error] #{error}\n" if error
|
115
|
-
else
|
116
|
-
$stderr.print "[Error] #{error}\n" if error
|
52
|
+
buffer.string.lines.each { |line| $stdout.print prefix + line.gsub("\r", '') } if jobs > 1
|
53
|
+
rescue => e
|
54
|
+
buffer.string.lines.each { |line| $stdout.print prefix + line.gsub("\r", '') } if jobs > 1
|
55
|
+
raise e
|
117
56
|
end
|
118
57
|
|
119
58
|
session
|
120
59
|
end
|
121
60
|
end
|
122
61
|
|
62
|
+
def run(args)
|
63
|
+
config = Hash.new
|
64
|
+
config[:runner] = self.method(:tty)
|
65
|
+
config[:inventory] = DEFAULT_INVENTORY_FILE
|
66
|
+
config[:templates] = DEFAULT_TEMPLATE_DIR
|
67
|
+
|
68
|
+
parser = OptionParser.new
|
69
|
+
parser.banner = "#{File.basename($0)} HOST_PATTERN [Options]"
|
70
|
+
parser.version = Xlogin::VERSION
|
71
|
+
|
72
|
+
parser.on('-i PATH', '--inventory', String, 'The PATH to the inventory file (default: $HOME/.xloginrc).')
|
73
|
+
parser.on('-T PATH', '--template', String, 'The PATH to the template dir (default: $HOME/.xlogin.d).')
|
74
|
+
parser.on('-L [DIRECTORY]', '--log-dir', String, 'The PATH to the log dir (default: $PWD).') { |v| v || '.' }
|
75
|
+
|
76
|
+
parser.on('-t', '--tty', TrueClass, 'Allocate a pseudo-tty.') { |v| config[:runner] = self.method(:tty) }
|
77
|
+
parser.on('-l', '--list', TrueClass, 'List the inventory.') { |v| config[:runner] = self.method(:list) }
|
78
|
+
parser.on('-e COMMAND', '--exec', 'Execute commands and quit.') { |v| config[:runner] = self.method(:exec); v }
|
79
|
+
|
80
|
+
parser.on('-j NUM', '--jobs', Integer, 'The NUM of jobs to execute in parallel(default: 1).')
|
81
|
+
parser.on('-E', '--enable', TrueClass, 'Try to gain enable priviledge.')
|
82
|
+
parser.on('-y', '--assume-yes', TrueClass, 'Automatically answer yes to prompts.')
|
83
|
+
|
84
|
+
parser.parse!(args, into: config)
|
85
|
+
config[:pattern] = args
|
86
|
+
|
87
|
+
Xlogin.configure do
|
88
|
+
set assume_yes: true if config[:assume_yes]
|
89
|
+
source File.expand_path(config[:inventory], ENV['PWD'])
|
90
|
+
template File.expand_path(config[:templates], ENV['PWD'])
|
91
|
+
end
|
92
|
+
raise "No host found: `#{args.join(', ')}`" if Xlogin.list(*config[:pattern]).empty?
|
93
|
+
|
94
|
+
config[:runner].call(config)
|
95
|
+
rescue => e
|
96
|
+
$stderr.puts e, '', parser
|
97
|
+
exit 1
|
98
|
+
end
|
99
|
+
|
123
100
|
end
|
124
101
|
end
|
data/lib/xlogin/delegator.rb
CHANGED
@@ -15,7 +15,7 @@ module Xlogin
|
|
15
15
|
|
16
16
|
login = @methods.fetch(:login)
|
17
17
|
delegate = @methods.fetch(:delegate)
|
18
|
-
raise
|
18
|
+
raise Xlogin::Error.new("'login' and 'delegate' methods must be defined in the #{target_info[:type]} template.") unless login && delegate
|
19
19
|
|
20
20
|
relay_uri = Addressable::URI.parse(uri.to_s)
|
21
21
|
userinfo_cache = relay_uri.userinfo.dup
|
data/lib/xlogin/factory.rb
CHANGED
@@ -52,7 +52,7 @@ module Xlogin
|
|
52
52
|
|
53
53
|
def build(type:, **opts)
|
54
54
|
template = get_template(type)
|
55
|
-
raise
|
55
|
+
raise Xlogin::Error.new("Template not found: '#{type}'") unless template
|
56
56
|
|
57
57
|
template.build(uri(opts), **opts)
|
58
58
|
end
|
@@ -63,19 +63,17 @@ module Xlogin
|
|
63
63
|
|
64
64
|
def build_from_hostname(args, **opts)
|
65
65
|
hostinfo = get_inventory(args)
|
66
|
-
raise
|
66
|
+
raise Xlogin::Error.new("Host not found: '#{args}'") unless hostinfo
|
67
67
|
|
68
68
|
build(hostinfo.merge(name: args, **opts))
|
69
69
|
end
|
70
70
|
|
71
|
-
def method_missing(method_name, *args, &block)
|
72
|
-
super unless args.size == 2
|
71
|
+
def method_missing(method_name, *args, **opts, &block)
|
72
|
+
super unless args.size == 2 && URI.regexp =~ args[1]
|
73
73
|
|
74
74
|
type = method_name.to_s.downcase
|
75
75
|
name = args[0]
|
76
76
|
uri = args[1]
|
77
|
-
opts = args[2] || {}
|
78
|
-
|
79
77
|
set_inventory(name, type: type, uri: uri, **opts)
|
80
78
|
end
|
81
79
|
|
@@ -90,7 +88,7 @@ module Xlogin
|
|
90
88
|
|
91
89
|
Addressable::URI.parse("#{scheme}://" + [userinfo, address].compact.join('@'))
|
92
90
|
rescue
|
93
|
-
raise
|
91
|
+
raise Xlogin::Error.new("Invalid target - '#{opts}'")
|
94
92
|
end
|
95
93
|
|
96
94
|
end
|
data/lib/xlogin/version.rb
CHANGED
data/xlogin.gemspec
CHANGED
@@ -29,6 +29,7 @@ Gem::Specification.new do |spec|
|
|
29
29
|
spec.bindir = "bin"
|
30
30
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
31
31
|
spec.require_paths = ["lib"]
|
32
|
+
spec.required_ruby_version = ">= 2.4.1"
|
32
33
|
|
33
34
|
spec.add_dependency "net-telnet"
|
34
35
|
spec.add_dependency "net-ssh"
|
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.
|
4
|
+
version: 0.13.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- haccht
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-09-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: net-telnet
|
@@ -187,7 +187,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
187
187
|
requirements:
|
188
188
|
- - ">="
|
189
189
|
- !ruby/object:Gem::Version
|
190
|
-
version:
|
190
|
+
version: 2.4.1
|
191
191
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
192
192
|
requirements:
|
193
193
|
- - ">="
|