無料で使えるシステムトレードフレームワーク「Jiji」 をリリースしました!

・OANDA Trade APIを利用した、オープンソースのシステムトレードフレームワークです。
・自分だけの取引アルゴリズムで、誰でも、いますぐ、かんたんに、自動取引を開始できます。

マルチキャストSSHクライアント

入力されたコマンドを複数のサーバーに送信して実行するスクリプトを試作しました。

  • Rubynet/sshを使って複数のサーバーに並列でつなぎ、
  • 入力されたコマンドをすべてのサーバーに送付/実行し、結果を順番に出力。
  • 接続先ホストやユーザー名はyamlファイルからロードします。

・・・やっていることは基本的にこれだけなので、いろいろ問題があります。

  • historyを使おうとして↑を押すと表示が乱れる。
  • バックスペースを押すと表示が乱れる。
  • タブを押すと(ry
  • Ctrl+Cを押すと、スクリプト自体が終了してしまう。

まぁ、バッチ処理をさせるぶんには何とかなるかも?

使い方

設定ファイル("hosts.yaml")に接続先サーバーとユーザー/パスワードの一覧を平文で書きます。

---
- host: xxx.com
  user: admin
  pass: hogehoge

- host: yyy.com
  user: admin
  pass: foovar

設定ファイルがあるディレクトリでスクリプトを実行。

$ ruby ssh-multicast.rb 

しばらくすると「>」が表示されるので、すかさずコマンドを打ち込みます!

Last login: Wed Feb 13 18:37:22 2008 from aaa.co.jp
Last login: Wed Feb 13 18:29:27 2008 from aaa.co.jp

> ps aux | grep bash

サーバーごとの実行結果がつらつらと表示されます。

ps auxf | grep bash
foo 16485  0.0  0.0  7044 1472 pts/0    Ss   18:51   0:00          \_ -bash
foo 16877  0.0  0.0  6104  752 pts/0    S+   18:55   0:00              \_ grep bash
[admin@xxx ~]$ 
----------------
ps auxf | grep bash
foo 15105  0.0  0.0   5564  1480 pts/1    Ss+  12:08   0:00  |       \_ -bash
foo 15196  0.0  0.0   5560  1480 pts/2    Ss   12:08   0:00  |       \_ -bash
foo 26758  0.0  0.0   5560  1488 pts/3    Ss   18:43   0:00          \_ -bash
foo 27264  0.0  0.0   4956   752 pts/3    S+   18:47   0:00              \_ grep bash
[admin@yyy ~]$ 

>

依存モジュール

以下のモジュールを利用しています。別途インストールしてください。

$ gem install --remote net-ssh
$ gem install --remote termios

実装

実装は以下。net/sshのサンプルをちょっと改造しただけだったりするのは秘密だ。

require 'rubygems'
require 'net/ssh'

begin
  require 'termios'
rescue LoadError
end

def stdin_buffer( enable )
  return unless defined?( Termios )
  attr = Termios::getattr( $stdin )
  if enable
    attr.c_lflag |= Termios::ICANON | Termios::ECHO
  else
    attr.c_lflag &= ~(Termios::ICANON|Termios::ECHO)
  end
  Termios::setattr( $stdin, Termios::TCSANOW, attr )
end

def shell_loop( shells )
  shells.each {|shell|
      $stdout.print shell.stdout while shell.stdout?
  }
  $stdout.print "\n> "
  $stdout.flush
  loop {
    shells.reject! {|shell| !shell.open? }
    break if shells.empty?
    data = ""
    if IO.select([$stdin],nil,nil,0.01)
      data = $stdin.sysread(1)
      shells.each {|shell|
        shell.send_data data
      }
    end

    next if data == nil || data.size <= 0

    if ( data =~ /[\w \+\-|]+/ )
      $stdout.print data
    else
      shells.each {|shell|
          $stdout.print"\n----------------\n"
          $stdout.print shell.stdout while shell.stdout?
      }
      $stdout.print "\n> "
    end
    $stdout.flush
  }
end

hosts = YAML.load_file("./hosts.yaml")
sessions = []
shells = []

begin
  hosts.each {|h|
    sessions << Net::SSH.start( h['host'], h['user'], h['pass'] )
  }
  begin
    stdin_buffer false
    sessions.each {|session|
      shells << session.shell.open( :pty => true )
    }
    shell_loop shells
  ensure
    stdin_buffer true
  end
ensure
  sessions.each {|session|
    begin
      session.close
    ensure Exception
      puts $!
    end
  }
end