tmux-cssh 0.2.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 +7 -0
- data/.gitignore +15 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +0 -0
- data/README.md +80 -0
- data/README_ja.md +99 -0
- data/Rakefile +0 -0
- data/VERSION +1 -0
- data/bin/tssh +104 -0
- data/lib/tssh.rb +72 -0
- data/tmux-cssh.gemspec +13 -0
- metadata +68 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 5e98cc319a8b74ce0ef895d7ca295621028ae6e6
|
4
|
+
data.tar.gz: 0dab99e91d4471541ecaa78269acf97ad8d26525
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 70ec48338850dc68cad71debe431be1cfd0ae530f1d57ab10d9cba7d91a5cf21c40b5c64f617c250795f8b37f3369cd7da73c518220b7c3dd3504c9abca3f506
|
7
|
+
data.tar.gz: 03e40a19d8af851efc3bac78a26b05219abf2f2a770a9546a9d263574a70031a9d05741a2c64d154595ba633cdf855adbca35a9c7ad02f95c46fb7ff8c85c883
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
File without changes
|
data/README.md
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
# tmux-cssh-rb
|
2
|
+
|
3
|
+
## Requirements
|
4
|
+
|
5
|
+
* Ruby 1.9.3 or later
|
6
|
+
* tmux
|
7
|
+
|
8
|
+
## Installing
|
9
|
+
|
10
|
+
### Install from RubyGems repository
|
11
|
+
|
12
|
+
coming soon...
|
13
|
+
|
14
|
+
### Build gem and install
|
15
|
+
|
16
|
+
```sh
|
17
|
+
git clone https://github.com/yshh/tmux-cssh-rb.git path/to/tmux-cssh-rb
|
18
|
+
cd path/to/tmux-cssh-rb
|
19
|
+
gem build tmux-cssh.gemspec
|
20
|
+
gem install tmux-cssh-*.gem
|
21
|
+
```
|
22
|
+
|
23
|
+
### Use Bundler
|
24
|
+
|
25
|
+
Write the line below to your Gemfile:
|
26
|
+
```ruby
|
27
|
+
gem 'tmux-cssh', git: 'https://github.com/yshh/tmux-cssh-rb.git'
|
28
|
+
```
|
29
|
+
|
30
|
+
## Usage
|
31
|
+
|
32
|
+
```
|
33
|
+
% tssh -h
|
34
|
+
Usage: tssh [options]
|
35
|
+
-l, --login USERNAME
|
36
|
+
-c, --config FILE
|
37
|
+
--ssh SSH_COMMAND
|
38
|
+
--ssh_args SSH_ARGUMENTS
|
39
|
+
--debug DEBUG_LEVEL
|
40
|
+
--panes_per_window NUM
|
41
|
+
```
|
42
|
+
|
43
|
+
`tssh` should be executed outside of tmux session.
|
44
|
+
|
45
|
+
```sh
|
46
|
+
tssh user@host1 host2 host3
|
47
|
+
```
|
48
|
+
|
49
|
+
### Sync mode
|
50
|
+
|
51
|
+
When you type to pane, key strokes are copied to all panes in the window.
|
52
|
+
Use tmux command `set-window-option synchronize-panes` to disable this mode.
|
53
|
+
|
54
|
+
You may want to bind a shortcut key to this command (add the line below to your ~/.tmux.conf)
|
55
|
+
|
56
|
+
```
|
57
|
+
bind-key g setw synchronize-panes
|
58
|
+
```
|
59
|
+
|
60
|
+
### How to exit
|
61
|
+
|
62
|
+
Panes do not close after the ssh session terminates
|
63
|
+
(using `set remain-on-exit`) so that you can see error messages in the case
|
64
|
+
ssh session terminated unexpectedly by errors such as connection error and
|
65
|
+
authentication failure.
|
66
|
+
|
67
|
+
You can close dead panes with tmux commands:
|
68
|
+
|
69
|
+
* `kill-pane` (closes selected pane; bound to `C-b x` by default) or
|
70
|
+
* `kill-window` (closes all panes in current window; bound to `C-b &` by default)
|
71
|
+
|
72
|
+
## OS limits
|
73
|
+
|
74
|
+
Opening many tmux panes hits some OS limits.
|
75
|
+
|
76
|
+
* num of pty
|
77
|
+
* max open files
|
78
|
+
|
79
|
+
## Known issues
|
80
|
+
|
data/README_ja.md
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
# tmux-cssh-rb
|
2
|
+
|
3
|
+
## 必要環境
|
4
|
+
|
5
|
+
* Ruby 1.9.3 以上
|
6
|
+
* tmux
|
7
|
+
|
8
|
+
## インストール
|
9
|
+
|
10
|
+
### gem コマンド
|
11
|
+
|
12
|
+
TODO: RubyGems リポジトリに上げる
|
13
|
+
|
14
|
+
GitHub リポジトリから直接インストールする場合は以下を実行してください。
|
15
|
+
|
16
|
+
```sh
|
17
|
+
gem install specific_install
|
18
|
+
gem specific_install https://github.com/yshh/tmux-cssh-rb.git
|
19
|
+
```
|
20
|
+
|
21
|
+
### ビルド・インストール
|
22
|
+
|
23
|
+
```sh
|
24
|
+
git clone https://github.com/yshh/tmux-cssh-rb.git path/to/tmux-cssh-rb
|
25
|
+
cd path/to/tmux-cssh-rb
|
26
|
+
gem build tmux-cssh.gemspec
|
27
|
+
gem install tmux-cssh-*.gem
|
28
|
+
```
|
29
|
+
|
30
|
+
### Bundler を使う
|
31
|
+
|
32
|
+
Gemfile に以下を1行追加してください。
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
gem 'tmux-cssh', git: 'https://github.com/yshh/tmux-cssh-rb.git'
|
36
|
+
```
|
37
|
+
|
38
|
+
## 使い方
|
39
|
+
|
40
|
+
`tssh` は tmux セッションの外で実行する必要があります。
|
41
|
+
tmux セッション内での実行は現状サポート(?)外です。
|
42
|
+
|
43
|
+
```sh
|
44
|
+
tssh user@host1 host2 host3
|
45
|
+
```
|
46
|
+
|
47
|
+
`-h` オプションをつけて実行するとヘルプが表示されます。
|
48
|
+
|
49
|
+
```
|
50
|
+
% tssh -h
|
51
|
+
Usage: tssh [options]
|
52
|
+
-l, --login USERNAME
|
53
|
+
-c, --config FILE
|
54
|
+
--ssh SSH_COMMAND
|
55
|
+
--ssh_args SSH_ARGUMENTS
|
56
|
+
--debug DEBUG_LEVEL
|
57
|
+
--panes_per_window NUM
|
58
|
+
```
|
59
|
+
|
60
|
+
### Sync モード
|
61
|
+
|
62
|
+
ひとつの pane にタイプした文字は、window 内の全ての pane にコピーされます。
|
63
|
+
これは tmux の機能を使っており、
|
64
|
+
tmux コマンド `set-window-option synchronize-panes` で無効化できます。
|
65
|
+
この tmux コマンドにショートカットキーを割り当てると便利です。
|
66
|
+
|
67
|
+
例えば以下の tmux コマンドを ~/.tmux.conf に追加する:
|
68
|
+
|
69
|
+
```
|
70
|
+
bind-key g setw synchronize-panes
|
71
|
+
```
|
72
|
+
|
73
|
+
詳細については tmux のマニュアルを参照してください。
|
74
|
+
|
75
|
+
### 終了方法
|
76
|
+
|
77
|
+
pane は ssh セッションの終了後に自動的には終了しません
|
78
|
+
(tmux の `set-window-option remain-on-exit` を設定しています)。
|
79
|
+
認証えらーなどでセッションが意図せずエラー終了した際、エラーメッセージが
|
80
|
+
消えてしまうのを防ぐためです。
|
81
|
+
|
82
|
+
pane は以下の tmux コマンドで閉じることができます。
|
83
|
+
|
84
|
+
* `kill-pane` (選択中の pane を閉じる; デフォルト: `C-b x`)
|
85
|
+
* `kill-window` (window 上の全ての pane を閉じる; デフォルト: `C-b &`)
|
86
|
+
|
87
|
+
## OS の制限
|
88
|
+
|
89
|
+
たくさんの tmux pane を開くと、OS の各種上限に当たります。
|
90
|
+
|
91
|
+
* pty 数
|
92
|
+
* max open files
|
93
|
+
|
94
|
+
TODO: 回避方法
|
95
|
+
|
96
|
+
## 既知の問題
|
97
|
+
|
98
|
+
XXX
|
99
|
+
|
data/Rakefile
ADDED
File without changes
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/bin/tssh
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'logger'
|
4
|
+
require 'inifile'
|
5
|
+
require 'optparse'
|
6
|
+
require 'tssh'
|
7
|
+
|
8
|
+
# default options
|
9
|
+
options = {
|
10
|
+
login: nil,
|
11
|
+
config: "#{ENV['HOME']}/.tsshrc",
|
12
|
+
# tile_x: nil,
|
13
|
+
# tile_y: nil,
|
14
|
+
# ssh: '/usr/local/bin/proxychains4 ssh',
|
15
|
+
ssh: 'ssh',
|
16
|
+
ssh_args: '',
|
17
|
+
# remote_command: nil,
|
18
|
+
# hosts: nil,
|
19
|
+
# session_max: 256,
|
20
|
+
# ping_test: nil,
|
21
|
+
# ping_timeout: nil,
|
22
|
+
# sock: nil,
|
23
|
+
# sorthosts: false,
|
24
|
+
# slave_settings_set: nil,
|
25
|
+
# master_settings_set: nil,
|
26
|
+
# interleave: nil,
|
27
|
+
debug: 0,
|
28
|
+
|
29
|
+
panes_per_window: 128,
|
30
|
+
}
|
31
|
+
|
32
|
+
args = {}
|
33
|
+
|
34
|
+
ARGV.options do |opt|
|
35
|
+
opt.on('-l', '--login USERNAME',
|
36
|
+
"SSH login name (default: #{options[:login]})"
|
37
|
+
) { |v| args[:login] = v }
|
38
|
+
opt.on('-c', '--config FILE',
|
39
|
+
"Config file path (default: #{options[:config]})"
|
40
|
+
) { |v|
|
41
|
+
raise "Config file '#{v}' does not exist" unless File.exists?(v)
|
42
|
+
args[:config] = v
|
43
|
+
}
|
44
|
+
opt.on('--ssh SSH_COMMAND',
|
45
|
+
"Custom SSH command (default: #{options[:config]})"
|
46
|
+
) { |v| args[:ssh] = v }
|
47
|
+
opt.on('--ssh_args SSH_ARGUMENTS') do |v|
|
48
|
+
args[:ssh_args] = v
|
49
|
+
end
|
50
|
+
opt.on('--debug DEBUG_LEVEL',
|
51
|
+
"Debug level [0-2] (default: #{options[:debug]})"
|
52
|
+
) { |v| args[:debug] = v.to_i }
|
53
|
+
opt.on("--panes_per_window NUM",
|
54
|
+
"Max panes opened on one window (default: #{options[:panes_per_window]})"
|
55
|
+
) { |v| args[:panes_per_window] = v.to_i }
|
56
|
+
opt.parse!
|
57
|
+
end
|
58
|
+
|
59
|
+
logger = Logger.new(STDERR)
|
60
|
+
|
61
|
+
# args "debug" and "config" are specially concerned
|
62
|
+
unless args[:debug].nil?
|
63
|
+
options[:debug] = args[:debug]
|
64
|
+
end
|
65
|
+
logger.level = case options[:debug]
|
66
|
+
when 0 then Logger::WARN
|
67
|
+
when 1 then Logger::INFO
|
68
|
+
when 2 then Logger::DEBUG
|
69
|
+
else raise "Invalid debug level: '#{options[:debug]}'"
|
70
|
+
end
|
71
|
+
|
72
|
+
unless args[:config].nil?
|
73
|
+
options[:config] = args[:config]
|
74
|
+
end
|
75
|
+
|
76
|
+
if not options[:config].nil? and File.exists?(options[:config])
|
77
|
+
logger.info "Using config file: #{options[:config]}"
|
78
|
+
# config file options overwrite the default options
|
79
|
+
config = IniFile.load(options[:config])
|
80
|
+
config['global'].each do |k, v|
|
81
|
+
# XXX
|
82
|
+
if %w(panes_per_window debug).include? k
|
83
|
+
options[k.to_sym] = v.to_i
|
84
|
+
else
|
85
|
+
options[k.to_sym] = v
|
86
|
+
end
|
87
|
+
logger.debug "option from config file: #{k} => #{v}"
|
88
|
+
end
|
89
|
+
else
|
90
|
+
logger.info "Config file #{options[:config]} does not exist"
|
91
|
+
end
|
92
|
+
|
93
|
+
# command_line args are the most prioritized
|
94
|
+
args.each do |k, v|
|
95
|
+
logger.debug "option from args: #{k} => #{v}"
|
96
|
+
options[k] = v
|
97
|
+
end
|
98
|
+
|
99
|
+
# XXX
|
100
|
+
hosts = ARGV
|
101
|
+
|
102
|
+
tssh = TmuxClusterSSH.new(options, hosts, logger)
|
103
|
+
tssh.run
|
104
|
+
|
data/lib/tssh.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
class TmuxClusterSSH
|
2
|
+
def initialize options, hosts, logger
|
3
|
+
@options = options
|
4
|
+
@hosts = hosts
|
5
|
+
@logger = logger
|
6
|
+
raise "No hosts specified" if @hosts.count == 0
|
7
|
+
end
|
8
|
+
|
9
|
+
def ssh_command host
|
10
|
+
if @options[:login].nil?
|
11
|
+
"#{@options[:ssh]} #{@options[:ssh_args]} #{host}"
|
12
|
+
else
|
13
|
+
"#{@options[:ssh]} #{@options[:ssh_args]} #{@options[:login]}@#{host}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def calc_num_panes(num)
|
18
|
+
# XXX should read tmux implementation
|
19
|
+
if num > @options[:panes_per_window]
|
20
|
+
return @options[:panes_per_window] + calc_num_panes(num - @options[:panes_per_window])
|
21
|
+
end
|
22
|
+
|
23
|
+
sqrt_num = Math.sqrt(num)
|
24
|
+
panes = sqrt_num.ceil * sqrt_num.floor
|
25
|
+
panes >= num ? panes : sqrt_num.ceil ** 2
|
26
|
+
end
|
27
|
+
|
28
|
+
def run_command cmd
|
29
|
+
@logger.info cmd
|
30
|
+
res = system cmd
|
31
|
+
case res
|
32
|
+
when nil
|
33
|
+
raise "command `#{cmd}` failed"
|
34
|
+
when false
|
35
|
+
raise "command `#{cmd}` exited with status #{$?}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def run
|
40
|
+
num_panes = calc_num_panes(@hosts.count)
|
41
|
+
session_name = "tssh-#{$$}"
|
42
|
+
(0..num_panes-1).each do |i|
|
43
|
+
host = @hosts[i]
|
44
|
+
if host.nil?
|
45
|
+
command = "'echo;echo empty'"
|
46
|
+
else
|
47
|
+
command = "'echo #{host};exec #{ssh_command(host)}'"
|
48
|
+
end
|
49
|
+
STDERR.puts command if @options[:debug] > 0
|
50
|
+
if i == 0
|
51
|
+
# create new session
|
52
|
+
run_command "tmux new -d -s #{session_name} #{command}"
|
53
|
+
run_command "tmux setw -t #{session_name} synchronize-panes on > /dev/null"
|
54
|
+
run_command "tmux setw -t #{session_name} remain-on-exit on > /dev/null"
|
55
|
+
elsif i % @options[:panes_per_window] == 0
|
56
|
+
# create new window on existing session
|
57
|
+
run_command "tmux neww -t #{session_name} #{command}"
|
58
|
+
run_command "tmux setw -t #{session_name} synchronize-panes on > /dev/null"
|
59
|
+
run_command "tmux setw -t #{session_name} remain-on-exit on > /dev/null"
|
60
|
+
else
|
61
|
+
# add pane to active window
|
62
|
+
run_command "tmux splitw -t #{session_name} #{command}"
|
63
|
+
run_command "tmux selectl -t #{session_name} tiled > /dev/null"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
# XXX
|
67
|
+
run_command 'tmux select-pane -t 0'
|
68
|
+
|
69
|
+
# attach to the session
|
70
|
+
exec "tmux attach -t #{session_name}"
|
71
|
+
end
|
72
|
+
end
|
data/tmux-cssh.gemspec
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'tmux-cssh'
|
3
|
+
s.version = '0.2.1'
|
4
|
+
s.licenses = ['MIT']
|
5
|
+
s.summary = 'Cluster-SSH on tmux'
|
6
|
+
s.homepage = 'https://github.com/yshh/tmux-cssh-rb'
|
7
|
+
s.authors = ['Yusuke Hagihara']
|
8
|
+
s.email = 'yusuke.hagihara@gmail.com'
|
9
|
+
s.files = `git ls-files`.split($/)
|
10
|
+
s.executables = s.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
11
|
+
s.add_dependency 'inifile', '~> 2.0'
|
12
|
+
s.required_ruby_version = '>= 1.9.3'
|
13
|
+
end
|
metadata
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tmux-cssh
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Yusuke Hagihara
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-06-16 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: inifile
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.0'
|
27
|
+
description:
|
28
|
+
email: yusuke.hagihara@gmail.com
|
29
|
+
executables:
|
30
|
+
- tssh
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- ".gitignore"
|
35
|
+
- Gemfile
|
36
|
+
- LICENSE.txt
|
37
|
+
- README.md
|
38
|
+
- README_ja.md
|
39
|
+
- Rakefile
|
40
|
+
- VERSION
|
41
|
+
- bin/tssh
|
42
|
+
- lib/tssh.rb
|
43
|
+
- tmux-cssh.gemspec
|
44
|
+
homepage: https://github.com/yshh/tmux-cssh-rb
|
45
|
+
licenses:
|
46
|
+
- MIT
|
47
|
+
metadata: {}
|
48
|
+
post_install_message:
|
49
|
+
rdoc_options: []
|
50
|
+
require_paths:
|
51
|
+
- lib
|
52
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: 1.9.3
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
requirements: []
|
63
|
+
rubyforge_project:
|
64
|
+
rubygems_version: 2.2.2
|
65
|
+
signing_key:
|
66
|
+
specification_version: 4
|
67
|
+
summary: Cluster-SSH on tmux
|
68
|
+
test_files: []
|