xlogin 0.7.16 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c724426e5a7a390655bde1100323aa82b3e7189e
4
- data.tar.gz: 2e1f06d01d84439fdbce0acd48487d1ffb6fec6d
3
+ metadata.gz: 1d9a1b26c54a0a1729282e93dca1abcc8dfc1336
4
+ data.tar.gz: 41c331ddf6bf109f0ef85b79d79f0f841dbb787c
5
5
  SHA512:
6
- metadata.gz: a878236af348f8b765472712079c09dc7847d6db5a0521c1913edd62fb4efd63cd24411386b320912c24ed504a56acf1b8a3267db8ba2c4ef2f63abf8869a234
7
- data.tar.gz: 6d48c122220ce879f30933a866773b9d2bd0879059cca7ea0419f7cb9b6aef6a08d6680f0e5629f8a44689e1efd4c0e85d1e6199775379e340c44908f325e6b8
6
+ metadata.gz: 5c472d2e2fbe03a9b2bcfc088fe3a5143ac0fa20253c53c07dc84604ebd4fdbab787343bbd8e06e1f5eea259d1ff8894055e6a7f3dc0e7ecc855d472584c660c
7
+ data.tar.gz: b46d85dca28cf7643e2c85fb517bf4c86a5a58418b76f88b3011436e2b4bdb04f1a2b1221c55a83923b3a33dcedfcbde2bd5de3f4591725e66a8725dae803636
data/Gemfile.lock ADDED
@@ -0,0 +1,32 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ xlogin (0.8.0)
5
+ net-ssh
6
+ net-ssh-gateway
7
+ net-ssh-telnet
8
+ net-telnet
9
+ parallel
10
+
11
+ GEM
12
+ remote: https://rubygems.org/
13
+ specs:
14
+ net-ssh (4.2.0)
15
+ net-ssh-gateway (2.0.0)
16
+ net-ssh (>= 4.0.0)
17
+ net-ssh-telnet (0.2.1)
18
+ net-ssh (>= 2.0.1)
19
+ net-telnet (0.1.1)
20
+ parallel (1.12.1)
21
+ rake (10.5.0)
22
+
23
+ PLATFORMS
24
+ ruby
25
+
26
+ DEPENDENCIES
27
+ bundler (~> 1.13)
28
+ rake (~> 10.0)
29
+ xlogin!
30
+
31
+ BUNDLED WITH
32
+ 1.16.1
data/README.md CHANGED
@@ -3,12 +3,12 @@ rancid clogin alternative.
3
3
 
4
4
  ## Usage
5
5
 
6
- ネットワークデバイスへのログインを自動化するツール群。
7
- `~/.xlogin.d/`へファームウェア毎の仕様を記述することで対象機器を拡大可能。
6
+ ネットワークデバイスへのログイン処理を自動化するツール群。  
7
+ `~/.xlogin.d/`にファームウェア毎のログイン仕様(template)を記述することで自働化対象機器を任意に設定可能。
8
+ templateの記述例は[lib/xlogin/templates](https://github.com/haccht/xlogin/tree/master/lib/xlogin/templates)を参照のこと。
8
9
 
9
-
10
- 各個別装置毎のログインのための認証情報は`~/.xloginrc`へ記述しておく。
11
- `.xloginrc`のフォーマットはDSL形式で下記の通り
10
+ 各個別装置毎のログインのための認証情報は`~/.xloginrc`へ記述する。  
11
+ `.xloginrc`のフォーマットはDSL形式で下記の通り。
12
12
 
13
13
  ~~~
14
14
  #hosttype hostname telnet_uri_scheme options
@@ -16,6 +16,18 @@ vyos 'vyos01', 'telnet://vagrant:vagrant@127.0.0.1:2200'
16
16
  vyos 'vyos02', 'telnet://vagrant:vagrant@127.0.0.1:2201'
17
17
  ~~~
18
18
 
19
+ 下記コマンドでvyos01へ自動ログインし、プロンプトをユーザに渡す。
20
+
21
+ ~~~sh
22
+ xlogin vyos01
23
+ ~~~
24
+
25
+ また下記コマンドでvyos01,vyos02へ同時に自動ログインし、コマンドを一括投入する。
26
+
27
+ ~~~sh
28
+ xlogin 'vyos*' exec 'show configuration command | no-more; exit' -j 2
29
+ ~~~
30
+
19
31
  ## Installation
20
32
 
21
33
  Add this line to your application's Gemfile:
data/lib/xlogin/cli.rb CHANGED
@@ -8,123 +8,110 @@ require 'stringio'
8
8
  module Xlogin
9
9
  class CLI
10
10
 
11
+ DEFAULT_INVENTORY_FILE = File.join(ENV['HOME'], '.xloginrc')
12
+ DEFAULT_TEMPLATE_DIR = File.join(ENV['HOME'], '.xlogin.d')
13
+
11
14
  def self.run(args = ARGV)
12
15
  config = getopts(args)
13
16
  client = Xlogin::CLI.new
14
-
15
- task = config.task.downcase.tr('-', '_')
16
- Xlogin::CLI.usage("Task not defined - #{task}") unless client.respond_to?(task)
17
- client.method(task).call(config)
17
+ client.method(config.taskname).call(config)
18
18
  end
19
19
 
20
20
  def self.getopts(args)
21
21
  config = OpenStruct.new(
22
- task: 'tty',
23
- hostlist: [],
24
- parallels: 1,
22
+ parallel: 1,
25
23
  inventory: nil,
26
24
  templates: [],
27
25
  )
28
26
 
29
27
  parser = OptionParser.new
30
- parser.banner += ' HOST-PATTERN'
31
-
32
- parser.on('-m TASK', '--task', String, 'Execute the TASK(default: tty).') { |v| config.task = v }
33
- parser.on('-a ARGS', '--args', String, 'The ARGS to pass to the task.') { |v| config.args = v }
34
-
35
- parser.on('-i PATH', '--inventory', String, 'The PATH to the inventory file (default: $HOME/.xloginrc).') { |v| config.inventory = v }
36
- parser.on('-t PATH', '--template', String, 'The PATH to the template file.') { |v| config.templates << v }
37
- parser.on('-T DIRECTORY', '--template-dir', String, 'The DIRECTORY to the template files.') { |v| config.templates += Dir.glob(File.join(v, '*.rb')) }
38
- parser.on('-l [DIRECTORY]', '--log', String, 'The DIRECTORY to the output log file (default: $PWD/log).') { |v| config.logdir = v || ENV['PWD'] }
39
-
40
- parser.on('-p NUM', '--parallels', Integer, 'The NUM of the threads. (default: 5).') { |v| config.parallels = v }
41
- parser.on('-e', '--enable', TrueClass, 'Try to gain enable priviledge.') { |v| config.enable = v }
42
- parser.on('-y', '--assume-yes', TrueClass, 'Always answer "yes" if confirmed.') { |v| config.assume_yes = v }
43
- parser.on('-h', '--help', 'Show this message.') { Xlogin::CLI.usage }
44
-
45
- self.class.module_eval do
46
- define_method(:usage) do |message = nil|
47
- puts message if message
48
- puts parser.to_s
49
- exit 1
28
+ parser.banner = "#{File.basename($0)} HOST [TASK ARGUMENTS] [Options]"
29
+ parser.version = Xlogin::VERSION
30
+
31
+ parser.on('-i PATH', '--inventory', String, 'The PATH to the inventory file(default: $HOME/.xloginrc).') { |v| config.inventory = v }
32
+ parser.on('-t PATH', '--template', String, 'The PATH to the template file.') { |v| config.templates << v }
33
+ parser.on('-T DIRECTORY', '--template-dir', String, 'The DIRECTORY of the template files.') { |v| config.templates += Dir.glob(File.join(v, '*.rb')) }
34
+ parser.on('-L [DIRECTORY]', '--log-dir', String, 'The DIRECTORY of the log files(default: $PWD).') { |v| config.logdir = v || '.' }
35
+
36
+ parser.on('-j NUM', '--jobs', Integer, 'The NUM of jobs to execute in parallel(default: 1).') { |v| config.parallel = v }
37
+ parser.on('-e', '--enable', TrueClass, 'Try to gain enable priviledge.') { |v| config.enable = v }
38
+ parser.on('-y', '--assume-yes', TrueClass, 'Automatically answer yes to prompts.') { |v| config.authorize = v }
39
+
40
+ begin
41
+ args = parser.parse!(args)
42
+ hostlist = args.shift
43
+ taskname = args.shift || 'tty'
44
+
45
+ Xlogin.configure do
46
+ config.inventory ||= DEFAULT_INVENTORY_FILE
47
+ if config.templates.empty?
48
+ generate_templates(DEFAULT_TEMPLATE_DIR) unless File.exist?(DEFAULT_TEMPLATE_DIR)
49
+ config.templates += Dir.glob(File.join(DEFAULT_TEMPLATE_DIR, '*.rb'))
50
+ end
51
+
52
+ authorize(config.authorize)
53
+ source(File.expand_path(config.inventory, ENV['PWD']))
54
+ load_templates(*config.templates.map { |file| File.expand_path(file, ENV['PWD']) })
50
55
  end
51
- end
52
56
 
53
- args = parser.parse(args)
54
- Xlogin.configure do
55
- template(*config.templates)
56
- source(config.inventory)
57
- authorize(config.assume_yes)
58
- end
57
+ config.hostlist = hostlist.to_s.split(',').flat_map { |pattern| Xlogin.factory.list(pattern) }
58
+ config.taskname = taskname.to_s.downcase.tr('-', '_')
59
+ config.arguments = args
59
60
 
60
- config.hostlist += args.flat_map do |target|
61
- hostlist = Xlogin.factory.list(target)
62
- hostlist.tap { |e| raise "Invalid inventory - #{target}" if e.empty? }
61
+ methods = Xlogin::CLI.instance_methods(false)
62
+ raise "No host found: `#{hostlist}`" if config.hostlist.empty?
63
+ raise "No task defined: `#{taskname}`" if config.taskname.empty? || methods.find_index(config.taskname.to_sym).nil?
64
+ rescue => e
65
+ $stderr.puts e, '', parser
66
+ exit 1
63
67
  end
64
68
 
65
- config.parallels = [config.parallels, config.hostlist.size].min
66
69
  config
67
70
  end
68
71
 
69
- def tty(config)
70
- config.hostlist = [config.hostlist.shift].compact
71
- Xlogin::CLI.usage('Invalid inventory.') if config.hostlist.empty?
72
-
73
- puts "Trying #{config.hostlist.first[:name]}..."
74
- puts "Escape character is '^]'."
72
+ def list(config)
73
+ width = config.hostlist.map { |e| e[:name].length }.max
74
+ $stdout.puts config.hostlist.map { |e| "#{e[:name].to_s.ljust(width)} #{e[:type]}" }.sort
75
+ end
75
76
 
76
- login(config) do |session|
77
- session.interact!
78
- end
77
+ def tty(config)
78
+ target = config.hostlist.sort_by { |e| e[:name] }.first
79
+ $stdout.puts "Trying #{target[:name]}...", "Escape character is '^]'."
80
+ config.hostlist = [target]
81
+ login(config) { |session| session.interact! }
79
82
  end
80
83
 
81
84
  def exec(config)
82
- Xlogin::CLI.usage('Invalid inventory.') if config.hostlist.empty?
83
-
84
- login(config) do |session|
85
- command_lines = ['', *config.args.split(';')]
86
- command_lines.each { |command| session.cmd(command) }
87
- end
85
+ command_lines = ['', *config.arguments.flat_map { |e| e.split(';') }].map(&:strip)
86
+ login(config) { |session| command_lines.each { |command| session.cmd(command) } }
88
87
  end
89
88
 
90
89
  def load(config)
91
- Xlogin::CLI.usage('Invalid inventory.') if config.hostlist.empty?
92
-
93
- login(config) do |session|
94
- command_lines = ['', *IO.readlines(config.args.to_s)]
95
- command_lines.each { |command| session.cmd(command) }
96
- end
97
- end
98
-
99
- def list(config)
100
- config.hostlist += Xlogin.factory.list('all') if config.hostlist.empty?
101
- width = config.hostlist.map { |e| e[:name].length }.max
102
- puts config.hostlist.map { |e| "#{e[:name].to_s.ljust(width)} #{e[:type]}" }.sort
90
+ command_lines = ['', *config.arguments.flat_map { |e| IO.readlines(e) }].map(&:strip)
91
+ login(config) { |session| command_lines.each { |command| session.cmd(command) } }
103
92
  end
104
93
 
105
94
  private
106
95
  def login(config, &block)
107
- buffer = StringIO.new
108
-
109
- Parallel.map(config.hostlist, in_thread: config.parallels) do |hostinfo|
96
+ Parallel.map(config.hostlist, in_threads: config.parallel) do |hostinfo|
110
97
  begin
98
+ buffer = StringIO.new
111
99
  hostname = hostinfo[:name]
112
100
 
113
101
  loggers = []
114
- loggers << buffer if config.parallels != 1
115
- loggers << $stdout if config.parallels == 1
116
- loggers << File.join(config.logdir, "#{hostname}.log") if config.logdir
102
+ loggers << ((config.parallel > 1)? buffer : $stdout)
103
+ loggers << File.expand_path(File.join(config.logdir, "#{hostname}.log"), ENV['PWD']) if config.logdir
117
104
 
118
105
  session = Xlogin.get(hostinfo.merge(log: loggers))
119
- session.enable if session.config.enable && config.enable
106
+ session.enable if config.enable && hostinfo[:enable]
120
107
 
121
108
  block.call(session)
122
109
  rescue => e
123
- lines = (config.parallels > 1)? ["\n#{hostname}\t| [Error] #{e}"] : ["\n[Error] #{e}"]
110
+ lines = (config.parallel > 1)? ["\n#{hostname}\t| [Error] #{e}"] : ["\n[Error] #{e}"]
124
111
  lines.each { |line| $stderr.print "#{line.chomp}\n" }
125
112
  end
126
113
 
127
- if config.parallels > 1
114
+ if config.parallel > 1
128
115
  lines = buffer.string.lines.map { |line| "#{hostname}\t| " + line.gsub("\r", '') }
129
116
  lines.each { |line| $stdout.print "#{line.chomp}\n" }
130
117
  end
@@ -10,12 +10,11 @@ module Xlogin
10
10
  def initialize
11
11
  @database = Hash.new
12
12
  @templates = Hash.new
13
- @group = nil
14
13
  end
15
14
 
16
15
  def source(*files)
17
16
  files.compact.each do |file|
18
- file = File.expand_path(file)
17
+ raise SessionError.new("Inventory file not found: #{file}") unless File.exist?(file)
19
18
  instance_eval(IO.read(file)) if File.exist?(file)
20
19
  end
21
20
  end
@@ -29,19 +28,17 @@ module Xlogin
29
28
  @database[name]
30
29
  end
31
30
 
32
- def list(name = nil)
33
- keys = @database.keys
34
- keys = keys.select { |key| key =~ /^#{name}(:|$)/ } unless name.nil? || name.to_s == 'all'
35
- @database.values_at(*keys)
31
+ def list(pattern = nil)
32
+ key, val = pattern.to_s.split(':')
33
+ key, val = 'name', (key || '*') if val.nil?
34
+ @database.values.select { |info| File.fnmatch(val, info[key.to_sym]) }
36
35
  end
37
36
 
38
37
  def source_template(*files)
39
38
  files.compact.each do |file|
40
- file = File.expand_path(file)
39
+ raise TemplateError.new("Template file not found: #{file}") unless File.exist?(file)
41
40
  name = File.basename(file, '.rb').scan(/\w+/).join('_')
42
- next unless File.exist?(file)
43
-
44
- set_template(name, IO.read(file))
41
+ set_template(name, IO.read(file)) if File.exist?(file)
45
42
  end
46
43
  end
47
44
 
@@ -59,13 +56,6 @@ module Xlogin
59
56
  @templates.keys
60
57
  end
61
58
 
62
- def group(group_name)
63
- current_group = @group
64
- @group = [current_group, group_name.to_s].compact.join(':')
65
- yield
66
- @group = current_group
67
- end
68
-
69
59
  def build(type:, uri:, **opts)
70
60
  template = get_template(type)
71
61
  template.build(uri, **opts)
@@ -82,7 +72,7 @@ module Xlogin
82
72
  super unless caller_locations.first.label == 'block in source' and args.size >= 2
83
73
 
84
74
  type = method_name.to_s.downcase
85
- name = [@group, args.shift].compact.join(':')
75
+ name = args.shift
86
76
  uri = args.shift
87
77
  opts = args.shift || {}
88
78
  set(type: type, name: name, uri: uri, **opts)
@@ -1,3 +1,3 @@
1
1
  module Xlogin
2
- VERSION = "0.7.16"
2
+ VERSION = "0.8.0"
3
3
  end
data/lib/xlogin.rb CHANGED
@@ -6,10 +6,6 @@ require 'xlogin/version'
6
6
 
7
7
  module Xlogin
8
8
 
9
- DEFAULT_INVENTORY_FILE = File.join(ENV['HOME'], '.xloginrc')
10
- DEFAULT_TEMPLATE_DIR = File.join(ENV['HOME'], '.xlogin.d')
11
- BUILTIN_TEMPLATE_FILES = Dir.glob(File.join(File.dirname(__FILE__), 'xlogin', 'templates', '*.rb'))
12
-
13
9
  class SessionError < StandardError; end
14
10
  class TemplateError < StandardError; end
15
11
  class AuthorizationError < StandardError; end
@@ -45,29 +41,29 @@ module Xlogin
45
41
  @authorized == true
46
42
  end
47
43
 
44
+ def generate_templates(dir)
45
+ FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
46
+ builtin_templates = Dir.glob(File.join(File.dirname(__FILE__), 'xlogin', 'templates', '*.rb'))
47
+ builtin_templates.each { |file| FileUtils.cp(file, DEFAULT_TEMPLATE_DIR) }
48
+ end
49
+
48
50
  private
49
- def authorize(boolean = false, &block)
51
+ def authorize(boolean = true, &block)
50
52
  @authorized = boolean == true || (block && block.call == true)
51
53
  end
52
54
 
53
- def source(source_file = nil)
54
- factory.source(source_file || DEFAULT_INVENTORY_FILE)
55
+ def source(*source_files)
56
+ factory.source(*source_files)
55
57
  end
56
58
 
57
59
  def template(*template_dirs)
58
60
  files = template_dirs.flat_map { |dir| Dir.glob(File.join(dir, '*.rb')) }
59
- load_template(*files)
61
+ load_templates(*files)
60
62
  end
61
63
  alias_method :template_dir, :template
62
64
 
63
- def load_template(*template_files)
64
- return factory.source_template(*template_files) unless template_files.empty?
65
-
66
- unless Dir.exist?(DEFAULT_TEMPLATE_DIR)
67
- FileUtils.mkdir_p(DEFAULT_TEMPLATE_DIR)
68
- BUILTIN_TEMPLATE_FILES.each { |file| FileUtils.cp(file, DEFAULT_TEMPLATE_DIR) }
69
- end
70
- template_dir(DEFAULT_TEMPLATE_DIR)
65
+ def load_templates(*template_files)
66
+ factory.source_template(*template_files)
71
67
  end
72
68
 
73
69
  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.7.16
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - haccht
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-02-15 00:00:00.000000000 Z
11
+ date: 2018-03-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: net-telnet
@@ -121,6 +121,7 @@ files:
121
121
  - ".gitignore"
122
122
  - CODE_OF_CONDUCT.md
123
123
  - Gemfile
124
+ - Gemfile.lock
124
125
  - LICENSE.txt
125
126
  - README.md
126
127
  - Rakefile