tmux-cssh 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,15 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ _yardoc
8
+ coverage
9
+ lib/bundler/man
10
+ pkg
11
+ rdoc
12
+ spec/reports
13
+ test/tmp
14
+ test/version_tmp
15
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
File without changes
@@ -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
+
@@ -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
+
File without changes
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -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
+
@@ -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
@@ -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: []