diff --git a/.gitignore b/.gitignore index 112c01ac..c2aad65b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,4 @@ -Gemfile.lock -*.dvi -*.html -*.log -*.pdf -*.tex -*.key -*.epub -*.mobi \ No newline at end of file +/book.pdf +/book.xml +/index.html +vendor/bundle diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 00000000..3f4688b8 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,7 @@ +AllCops: + Include: + - 'tasks/*.rake' + - Guardfile + - Rakefile + Exclude: + - 'vendor/**/*' diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..894eb916 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,19 @@ +language: ruby + +script: + - bundle exec rake travis + +before_install: + # Prevent "Please tell me who you are" errors + - git config --global user.email "travis@travis-ci.org" + - git config --global user.name "Travis CI" + +after_success: + - bundle exec rake deploy + +rvm: + - 2.0.0 + +env: + global: + - secure: "fzg1kaZRs8/Cw7mqWMPbji/h5wQQLdHkyl6jAiJVxB1VctcBa0YixonSUBE2kTbx8oaVOg7cQ1+5O4/5+LrU7h3eHNdzxuaqyPb8swDLPKv3J6vqP3U5fpyYvBN0OXQp42rB6m8X7+VOUGTqTGoNZNLMs85r+CAu5rsVIYIpaQ0=" diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..6df407b6 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,4 @@ +# Changelog + +## 0.1.0 (8/31/2015) +* Hello Trema 章のドラフト rev.1 を追加. diff --git a/Gemfile b/Gemfile new file mode 100644 index 00000000..d80ecca3 --- /dev/null +++ b/Gemfile @@ -0,0 +1,12 @@ +source 'https://rubygems.org' + +gem 'rake' + +group :development, :test do + gem 'asciidoctor', require: false + gem 'coderay', require: false + gem 'guard', require: false + gem 'guard-rake', require: false + gem 'guard-rubocop', require: false + gem 'rubocop', require: false +end diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 00000000..0d677a96 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,71 @@ +GEM + remote: https://rubygems.org/ + specs: + asciidoctor (1.5.2) + ast (2.1.0) + astrolabe (1.3.1) + parser (~> 2.2) + coderay (1.1.0) + ffi (1.9.10) + formatador (0.2.5) + guard (2.13.0) + formatador (>= 0.2.4) + listen (>= 2.7, <= 4.0) + lumberjack (~> 1.0) + nenv (~> 0.1) + notiffany (~> 0.0) + pry (>= 0.9.12) + shellany (~> 0.0) + thor (>= 0.18.1) + guard-rake (1.0.0) + guard + rake + guard-rubocop (1.2.0) + guard (~> 2.0) + rubocop (~> 0.20) + listen (3.0.3) + rb-fsevent (>= 0.9.3) + rb-inotify (>= 0.9) + lumberjack (1.0.9) + method_source (0.8.2) + nenv (0.2.0) + notiffany (0.0.7) + nenv (~> 0.1) + shellany (~> 0.0) + parser (2.2.2.6) + ast (>= 1.1, < 3.0) + powerpack (0.1.1) + pry (0.10.1) + coderay (~> 1.1.0) + method_source (~> 0.8.1) + slop (~> 3.4) + rainbow (2.0.0) + rake (10.4.2) + rb-fsevent (0.9.5) + rb-inotify (0.9.5) + ffi (>= 0.5.0) + rubocop (0.33.0) + astrolabe (~> 1.3) + parser (>= 2.2.2.5, < 3.0) + powerpack (~> 0.1) + rainbow (>= 1.99.1, < 3.0) + ruby-progressbar (~> 1.4) + ruby-progressbar (1.7.5) + shellany (0.0.1) + slop (3.6.0) + thor (0.19.1) + +PLATFORMS + ruby + +DEPENDENCIES + asciidoctor + coderay + guard + guard-rake + guard-rubocop + rake + rubocop + +BUNDLED WITH + 1.10.6 diff --git a/Guardfile b/Guardfile new file mode 100644 index 00000000..efc1d0e9 --- /dev/null +++ b/Guardfile @@ -0,0 +1,8 @@ +guard 'rake', task: :render do + watch(/.+\.adoc$/) +end + +guard :rubocop do + watch(/.+\.rb$/) + watch(%r{(?:.+/)?\.rubocop\.yml$}) { |m| File.dirname(m[0]) } +end diff --git a/README.md b/README.md index 15098043..b41e0e39 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,28 @@ +# Trema本 [![Build Status](http://img.shields.io/travis/yasuhito/trema-book/develop.svg?style=flat)][travis] [![Gitter](https://badges.gitter.im/Join%20Chat.svg)][gitter] + +Tremaを使ったOpenFlowプログラミングを解説するTrema本のリポジトリです. + + * Twitter: ハッシュタグは [#Trema本](https://twitter.com/hashtag/Trema%E6%9C%AC) です + +[travis]: http://travis-ci.org/yasuhito/trema-book +[gitter]: https://gitter.im/yasuhito/trema-book + + +## 書籍版 + ![カバー](https://raw.github.com/yasuhito/trema-book/master/cover.png "本のカバー") -### ご購入はこちらから +Amazon (http://www.amazon.co.jp/dp/4774154652/) などから購入できます. + + +## Installation + +[![Dependency Status](http://img.shields.io/gemnasium/yasuhito/trema-book.svg?style=flat)][gemnasium] - * Amazon: http://www.amazon.co.jp/dp/4774154652/ - * サポートサイト: http://yasuhito.github.com/trema-book/ +[gemnasium]: https://gemnasium.com/yasuhito/trema-book -### License +## License This book is released under the GNU General Public License version 3.0: diff --git a/Rakefile b/Rakefile index 4b1f6638..76173504 100644 --- a/Rakefile +++ b/Rakefile @@ -1,82 +1,6 @@ -# -*- coding: utf-8 -*- +task default: [:rubocop, :render] +task travis: [:rubocop, :render] -task :default => "ja:pdf" -task :pdf => "ja:pdf" -task :epub => "ja:epub" -task :mobi => "ja:mobi" -task :clean => "ja:clean" +task render: [:html, :pdf] - -def add_pdf_task lang, description - desc description - task :pdf => "clean:pdf" do - cd lang do - sh "review-pdfmaker trema.yaml" - end - end - - namespace :clean do - task :pdf do - sh "rm -f ./#{ lang }/trema.pdf" - end - end - task :clean => "#{ lang }:clean:pdf" -end - - -def add_epub_task lang, description - desc description - task :epub => "clean:epub" do - cd lang do - sh "review-epubmaker trema.yaml" - end - end - - namespace :clean do - task :epub do - sh "rm -f ./#{ lang }/trema.epub" - end - end - task :clean => "#{ lang }:clean:epub" -end - - -def add_mobi_task lang, description - desc description - task :mobi => [ :epub, "clean:mobi" ] do - if `which kindleGen`.empty? - raise "kindleGen is not installed!" - end - cd lang do - sh "KindleGen trema.epub" rescue nil - end - end - - namespace :clean do - task :mobi do - sh "rm -f ./#{ lang }/trema.mobi" - end - end - task :clean => "#{ lang }:clean:mobi" -end - - -namespace :ja do - add_pdf_task "ja", "PDF を生成" - add_epub_task "ja", "epub を生成" - add_mobi_task "ja", "mobi を生成" -end - - -namespace :en do - add_pdf_task "en", "Generates PDF" - add_epub_task "en", "Generates epub" - add_mobi_task "en", "Generates mobi" -end - - -### Local variables: -### mode: Ruby -### coding: utf-8 -### indent-tabs-mode: nil -### End: +Dir.glob('tasks/*.rake').each { |each| import each } diff --git a/book.adoc b/book.adoc new file mode 100644 index 00000000..648d17db --- /dev/null +++ b/book.adoc @@ -0,0 +1,13 @@ += TremaでOpenFlowプログラミング +高宮 安仁 ; 鈴木 一哉; 松井 暢之; 村木 暢哉; 山崎 泰宏 + +== OpenFlow って何? +// 仮置き + +include::hello_trema.adoc[leveloffset=+1] + +[appendix] +== Copyright and License +This book is released under the GNU General Public License version 3.0: + +- http://www.gnu.org/licenses/gpl-3.0.html diff --git a/en/CHAPS b/en/CHAPS deleted file mode 100644 index c9398a37..00000000 --- a/en/CHAPS +++ /dev/null @@ -1,23 +0,0 @@ - -whats_openflow.re -openflow_usecases.re -openflow_spec.re -openflow_frameworks.re - -openflow_framework_trema.re -switch_monitoring_tool.re -patch_panel.re -learning_switch.re -traffic_monitor.re -router_part1.re -router_part2.re -trema_architecture.re - -migrating_openflow.re -routing_switch.re -sliceable_switch.re -datacenter_wakame.re -google.re - -diy_switch.re -tdd.re \ No newline at end of file diff --git a/en/PART b/en/PART deleted file mode 100644 index 109ba02b..00000000 --- a/en/PART +++ /dev/null @@ -1,4 +0,0 @@ -Introduction to OpenFlow -OpenFlowプログラミング入門 -ケーススタディ - 本格的なOpenFlowアプリケーション -Appendix \ No newline at end of file diff --git a/en/images/openflow_framework_trema/trema_overview.png b/en/images/openflow_framework_trema/trema_overview.png deleted file mode 100644 index 27fdda6e..00000000 Binary files a/en/images/openflow_framework_trema/trema_overview.png and /dev/null differ diff --git a/en/images/switch_monitoring_tool/switch_ready.png b/en/images/switch_monitoring_tool/switch_ready.png deleted file mode 100644 index 902d8561..00000000 Binary files a/en/images/switch_monitoring_tool/switch_ready.png and /dev/null differ diff --git a/en/openflow_framework_trema.re b/en/openflow_framework_trema.re deleted file mode 100644 index 2fd65994..00000000 --- a/en/openflow_framework_trema.re +++ /dev/null @@ -1,400 +0,0 @@ -= OpenFlowフレームワークTrema - -//lead{ -Tremaを使うと楽しくOpenFlowプログラミングの世界が味わえます。これでいよいよあなたもSDNプログラマの仲間入りです! -//} - -//indepimage[izakaya][][width=10cm] - -== 作って分かるOpenFlow - -第II部では、いよいよ実際にOpenFlowでネットワークをプログラムする方法を紹介します。職場や自宅のような中小規模ネットワークでもすぐに試せる実用的なコードを通じて、「OpenFlowって具体的に何に使えるの?」「OpenFlowコントローラってどう作るの?」というよくある疑問に答えていきます。題材はなるべく実用例を取り上げるようにし、またOpenFlowやネットワークの基礎から説明していくようにしました。このためネットワークの専門家はもちろん、普通のプログラマにもすんなり理解できる内容となっているはずです。 - -まずは、この第II部で使うOpenFlowプログラミングのためのフレームワーク、@{Trema,トレマ}をあらためて紹介します。 - -== Tremaとは - -TremaはOpenFlowコントローラを開発するためのRuby向けプログラミングフレームワークです。GitHub上でオープンに開発しており、GPL2ライセンスのフリーソフトウェアです。公開は2011年の4月と比較的新しいソフトウェアですが、その使いやすさから国内外の企業や大学、および研究機関などですでに数多く採用されています。 - -Tremaの情報は主に次の URL から入手できます。 - - * Tremaホームページ:@{http://trema.github.com/trema/} - * GitHubのプロジェクトページ:@{http://github.com/trema/} - * メーリングリスト:@{http://groups.google.com/group/trema-dev} - * Twitterアカウント:@{http://twitter.com/trema_news} - -Tremaの特長をひとことで言うと、「Ruby on RailsやSinatraなどのWebフレームワークに影響を受けた、アジャイルなOpenFlowプログラミングフレームワークである」ということです。たとえば最近のアジャイル開発環境ではもはや当たり前ですが、Tremaは開発を効率化する便利なツールを数多く提供しています。このように開発サイクル全体の面倒を見てくれるところが Tremaの「フレームワーク」たるゆえんで、他のOpenFlow開発ツールとはまったく違うところです。 - -ここからは実際にこのTremaを使ってOpenFlowコントローラを作っていきます。まずはTremaをセットアップしましょう。 - -===[column] @{友太郎,ゆうたろう}の質問: Tremaの由来ってなに? - -よく「Tremaの由来って何ですか?」と聞かれるのですが、これは筆者の1人が大好きな大阪の「とれまレコード(@{http://www.fumiyatanaka.com/})」というレコードレーベルの名前から取りました。テクノミュージックを中心にリリースする小さなレーベルですが、DJの間では世界的に知られています。 - -さてそもそもこの"とれま"とはいったい何でしょう。これは日本がバブルの頃、道路の「とまれ」という標示がよく「とれま」と間違えて描かれていたという事実が元になっています。このありえない誤植の原因は、バブル景気時代に急増した外国人労働者が日本語もままならないまま道路工事現場で働いていたということにあるそうです。由来を探ってみると意外と面白い事実に行き着くことってありますね。 - -ちなみに、Tremaの公式ロゴマークは@{trema_logo}です。これはTwitterのTrema公式アカウント(@{@trema_news})のアイコンとしても使われています。 - -//image[trema_logo][Trema の公式ロゴマーク][width=5cm] - -もちろん、こんなに大胆な道路標識は日本中どこを探してもありません。この本の編集者が画像編集ソフトで試しに作ったところ評判が良かったので、そのまま公式ロゴになりました。 - -===[/column] - -== Tremaのセットアップ - -TremaはLinux上で動作します。次のディストリビューションとバージョンでの動作を保証しています。 - - * Ubuntu 10.04 以降 (i386/amd64, デスクトップ版) - * Debian GNU/Linux 6.0 (i386/amd64) - -なお保証はしていませんが、RedHatなどその他のLinuxディストリビューションでも動作するはずです。 - -Tremaの提供するtremaコマンドの実行にはroot権限が必要です。まずは、@{sudo}を使ってroot権限でコマンドを実行できるかどうか、@{sudo}の設定ファイルを確認してください。 - -@{sudo}が正しく設定できていることを確認したら、Tremaのインストールや実行に必要ないくつかのソフトウェアをインストールしましょう。 - - * Rubyインタプリタ@
{}Trema は RubyとCで作成されていて、アプリケーションの記述にもRubyを使います。TremaではRuby のバージョン 1.8.7 が必要です。1.8.6 以前のバージョンやバージョン 1.9 以降では今のところ動作しません。 - * Rubyのパッケージ管理システムRubyGems(@{https://rubygems.org/}) - * Trema本体。本書は、Tremaバージョン0.3.0を使用して執筆しています。 - * Tremaのコンパイルに必要な@{gcc}や@{make}、およびいくつかのライブラリ。 - -開発に必要なのはこれだけです。それでは、Ubuntuのパッケージ管理システム@{apt-get}を使って必要なパッケージを次のようにインストールしてください。もし他のディストリビューションを使いたい場合は、コマンド名とパッケージ名を適宜読み替えください@{ubuntu12.10}。 - -//footnote[ubuntu12.10][Ubuntu 12.10以降ではRubyのデフォルトバージョンがRuby1.9になっているので、@{ruby}, @{rubygems}, @{ruby-dev}をそれぞれTremaがサポートする@{ruby1.8}, @{rubygems1.8}, @{ruby-dev1.8}と読み替えてください。] - -//cmd{ -% sudo apt-get install gcc make ruby rubygems ruby-dev irb libpcap-dev libsqlite3-dev -//} - -以上でTremaをインストールするための準備が整いました。続いてTremaをインストールするには、RubyGemsのパッケージで手軽にインストールする方法と、最新のソースコードを取得して自分でビルドする方法があります。それぞれ説明していきましょう。 - -=== パッケージで手軽にインストールする場合 - -TremaはRubyGemsを使って次のようにコマンド一発で簡単にインストールできます。 - -//cmd{ -% gem install trema -//} - -RubyGemsでインストールした場合、自動的にTremaのコマンド@{trema}にパスが通っているはずです。次のコマンドでバージョンが表示されればインストールは成功です。 - -//cmd{ -% trema --version -trema version 0.3.0 -//} - -=== ソースコードから最新版をインストールする場合 - -最新版をインストールしたい人は、GitHubから自分でソースコードをダウンロードしてビルドすることもできます。まず、次のように@{git}を使って最新のソースコードを取得してください。 - -//cmd{ -% git clone git://github.com/trema/trema.git -//} - -次のコマンドを実行すると、Tremaが依存するRubyGemsのパッケージが自動的にインストールされます。 - -//cmd{ -% cd trema -% gem install bundler -% bundle install -//} - -次のコマンドでTremaをダウンロードしたディレクトリ以下にTremaがインストールされます。@{make install}のようなシステム全体へのインストール手順は不要ですので注意してください。 - -//cmd{ -% ./build.rb -//} - -次のコマンドで @{trema} コマンドが正しくインストールされたか確認してください。 - -//cmd{ -% ./trema --version -trema version 0.3.0 -//} - -もし必要あればこのディレクトリにパスを通し、@{trema}コマンドが簡単に起動できるようにしておいてください。 - -さあ、これでTremaによるOpenFlow開発環境が整いました。それでは早速、入門の定番Hello, WorldをTremaで書いてみましょう。 - -== Hello, Trema! - -今から書くアプリケーションは最も簡単なOpenFlowコントローラの一種で、画面に"Hello, Trema!"と表示するだけのものです。スイッチとはまったくやりとりしないスタンドアロンのアプリケーションですが、Tremaで作れるすべてのコントローラのテンプレートとなっています。 - -では、適当なディレクトリにエディタで@{hello-trema.rb}というファイルを開き、次のコードを入力してください。"@{.rb}"はRubyプログラムの標準的な拡張子です。なおRubyの文法は必要に応じておいおい説明しますので、もし分からなくても今のところは気にせずそのまま入力してください。 - -//emlist{ - class HelloTrema < Controller - def start - puts "Hello, Trema!" - end - end -//} - -意味はまだわからないかもしれませんが、とてもシンプルに書けることはわかると思います。それでは細かい文法は後で見るとして「習うより慣れろ」でさっそく実行してみましょう。 - -=== 実行してみよう(@{trema run}) - -作成したコントローラは@{trema run}コマンドですぐに実行できます。Rubyはインタプリタ言語なので、コンパイルの必要はありません。ターミナルで次のように入力すると、この世界一短いOpenFlowコントローラはフォアグラウンドプロセスとして起動し、画面に「@{Hello, Trema!}」と出力します。起動したコントローラは Ctrl + C で停止できます。 - -//cmd{ -% trema run ./hello-trema.rb -Hello, Trema! # Ctrl+c で終了 -% -//} - -いかがでしょうか?@{trema}コマンドを使うと、とても簡単にコントローラを実行できることがわかると思います。@{trema}コマンドには他にもいくつかの機能がありますのでここで簡単に紹介しておきましょう。 - -== @{trema コマンド} - -@{trema}コマンドはTrema唯一のコマンドラインツールであり、コントローラの起動やテストなど様々な用途に使います。たとえば先ほどの"Hello Trema!"で見たように、@{trema run}はコントローラを起動するためのコマンドです。起動したコントローラはOpenFlowスイッチと接続しメッセージをやりとりします。また、次の章以降で触れますが@{trema run}コマンドはオプションで仮想ネットワークを作ることもでき、作ったコントローラをこの仮想ネットワークの中でテストできます。このように、@{trema}コマンドはTremaフレームワークにおける中心的なツールで、あらゆるコントローラ開発の出発点と言えます(@{trema_overview})。 - -//image[trema_overview][@{trema}コマンドでコントローラを実ネットワークや仮想ネットワークで実行][width=12cm] - -@{trema}コマンドは@{git}や@{svn}コマンドと似たコマンド体系を持っており、@{trema}に続けて@{run}などのサブコマンドを指定することで様々な機能を呼び出します。こうしたコマンド体系を一般に「コマンドスイート」と呼びます。 - -一般的なコマンドスイートと同じく、サブコマンドの一覧は@{trema help}で表示できます。また、サブコマンド自体のヘルプは@{trema help [サブコマンド]}で表示できます。以下に@{trema help}で表示されるサブコマンド一覧をざっと紹介しておきましょう。いくつかのサブコマンドはまだ使い方を紹介していませんが、続く章で説明しますので今は目を通すだけでかまいません。 - -: @{trema run} - コントローラをフォアグラウンドで実行する。@{--daemonize (-d)}オプションを付けるとコントローラをバックグラウンド(デーモンモード)として実行できる - -: @{trema killall} - バックグラウンドで起動しているTremaプロセス全体を停止する - -: @{trema version} - Tremaのバージョンを表示する。@{trema --version}と同じ - -: @{trema ruby} - TremaのRuby APIをブラウザで表示する - -: @{trema kill [仮想スイッチ]} - 仮想ネットワーク内の指定したスイッチを停止する(@{switch_monitoring_tool}を参照) - -: @{trema up [仮想スイッチ]} - 仮想ネットワークの指定したスイッチを再起動する(@{switch_monitoring_tool}を参照) - -: @{trema send_packets [送信オプション]} - 仮想ネットワーク内でテストパケットを送信する(@{learning_switch}を参照) - -: @{trema show_stats [仮想ホスト名]} - 仮想ネットワーク内の仮想ホストで送受信したパケットの統計情報を表示する(@{learning_switch}を参照) - -: @{trema reset_stats} - 仮想ネットワーク内の仮想ホストで送受信したパケットの統計情報をリセットする(@{learning_switch}を参照) - -: @{trema dump_flows [仮想スイッチ名]} - 仮想ネットワーク内の仮想スイッチのフローテーブルを表示する(@{learning_switch}を参照) - -この章ではさきほど使った@{trema run}に加えて、Ruby APIを表示する@{trema ruby}コマンドを覚えておいてください。@{trema ruby}を実行するとデフォルトブラウザでTrema Ruby APIリファレンスのページが開きます(@{trema_ruby})。プログラミング中いつでもコマンド一発でリファレンスを開けるので大変便利です。 - -#@warn(API のページが YARD のせいで壊れているので、直してからスクリーンショット撮り直し) -//image[trema_ruby][@{trema ruby} コマンドで Trema Ruby API リファレンスを表示したところ][width=12cm] - -では、気になっていたRubyの文法にそろそろ進みましょう。第II部では今後もたくさんRubyを使いますが、その都度必要な文法を説明しますので心配はいりません。しっかりついてきてください。 - -== 即席 Ruby 入門 - -外国語の習得にも言えることですが、Rubyを習得する一番の近道は登場する品詞の種類を押さえておくことです。Rubyに出てくる名前(構成要素)には、その品詞を見分けるための手がかりとなる視覚的なヒントがかならずあります。名前に記号が使われていたり、最初の文字が大文字になっていたりするので、断片的なコードを見てもすぐにどんな品詞かわかります。品詞がわかれば、そのRubyコードがどんな構造かわかります。 - -これからそれぞれの品詞について簡単に説明しますが、最初からすべてが理解できなくとも構いません。しばらくすればHello, Trema!プログラムのあらゆる部分が識別できるようになっているはずです。 - -=== キーワード - -Ruby にはたくさんの組み込みの語があり、それぞれに意味が与えられています。これらの語を変数として使ったり、自分の目的に合わせて意味を変えたりはできません。 - -//quote{ -@{alias and BEGIN begin break case class def defined } -@{do else elsif END end ensure false for if } -@{in module next nil not or redo rescue retry } -@{return self super then true undef unless until when } -@{while yield} -//} - -このうち、「Hello Trema!」では@{class}と@{def}、そして@{end}キーワードを使いました。 - -//emlist{ -@{class} HelloTrema < Controller - @{def} start - puts "Hello, Trema!" - @{end} -@{end} -//} - -@{class}キーワードは続く名前(@{HelloTrema})のクラスを定義します。このクラス定義は最後の5行目の@{end}までです。@{def}キーワードは続く名前(@{start})のメソッドを定義します。このメソッド定義は4行目の@{end}までです。この@{def}や@{class}で始まって@{end}で終わる領域のことをブロックと呼びます。すべての Ruby プログラムはこのブロックがいくつか組み合わさったものです。 - -=== 定数 - -@{Time} や @{Array} や @{PORT_NUMBER} など、大文字で始まる名前が定数です。定数はRubyの世界では英語や日本語などの自然言語における固有名詞に当たります。 - -英語でも固有名詞は大文字で始めることになっています。たとえばTokyo Tower(東京タワー)もそうです。東京タワーは動かすことができませんし、何か別なものに勝手に変えることもできません。このように、固有名詞は時間とともに変化しないものを指します。そして固有名詞と同様、Rubyの定数は一度セットすると変更できません。 - -//quote{ -@{TokyoTower = "東京都港区芝公園4丁目2-8"} -//} - -「Hello Trema!」の例では@{class}キーワードに続く@{HelloTrema}と、@{Controller}がそれぞれ大文字で始まるので定数です。つまり、クラス名は定数なので実行中にその名前を変えることはできません。 - -//emlist{ -class @{HelloTrema} < @{Controller} - def start - puts "Hello, Trema!" - end -end -//} - -これで「Hello Trema!」の説明に必要な品詞の説明はおしまいです。それでは「Hello Trema!」の中身を読み解いていきましょう。 - -=== コントローラクラスの定義 - -キーワードの節で説明したように、@{class}キーワードに続く定数から@{end}までで定義されるブロックがクラス定義です。Tremaではすべてのコントローラはクラスとして定義され、かならずTremaの@{Controller}クラスを継承します。クラスを継承するには、 - -//quote{ -@{class クラス名 < 親クラス名} -//} - -//noindent -と書きます。 - -//emlist{ -@{class HelloTrema < Controller} - def start - puts "Hello, Trema!" - end -@{end} -//} - -@{Controller}クラスを継承することで、コントローラに必要な基本機能が@{HelloTrema}クラスにこっそりと追加されます。雑多な初期化などの裏仕事を@{Controller}クラスが代わりにやってくれるわけです。 - -=== ハンドラメソッドの定義 - -さて、こうして定義した@{HelloTrema}はどこから実行が始まるのでしょうか?Cで言う@{main()}関数に当たるものがどこにも見あたらない気がします。 - -その答はTremaの動作モデルであるイベントドリブンモデルにあります。Tremaのコントローラは、様々なOpenFlowイベントに反応するイベントハンドラをまとめたクラスとして定義できます。それぞれのイベントハンドラは、対応するOpenFlowイベントが発生したときに自動的に呼び出されます。たとえば何かOpenFlowメッセージが到着したとき、もしそのメッセージに対応するハンドラメソッドがコントローラクラスに定義されていれば、Tremaがそのメソッドを発見して呼んでくれます。 - -Tremaでよく使われるイベントをここにリストアップします。 - -#@warn(第 II 部で使うハンドラをここですべて説明) - -: @{start} - コントローラの起動時に呼ばれる - -: @{switch_ready}、@{switch_disconnected} - スイッチがコントローラに接続または切断したときに呼ばれる(@{switch_monitoring_tool}にて詳説) - -: @{packet_in} - 未知のパケットが到着したというPacket Inメッセージ到着時に呼ばれる(@{learning_switch}にて詳説) - -: @{flow_removed} - フローが消された時のFlow Removedメッセージ到着時に呼ばれる(@{traffic_monitor}にて詳説) - -ハンドラメソッドの定義は、@{def}キーワードに続く名前から@{end}までで定義されるブロックです。たとえば@{HelloTrema}の例では@{start}ハンドラメソッドを定義しており、これがコントローラの起動イベント発生時、つまり@{trema run}でコントローラを起動したときに自動的に呼ばれます。@{start}ハンドラメソッド中の@{puts}はRuby組込みのメソッドで、Cの@{puts()}関数と同じく文字列を標準出力へ改行付きで出力します。 - -//emlist{ -class HelloTrema < Controller - @{def start} - puts "Hello, Trema!" - @{end} -end -//} - -===[column] @{取間,とれま}先生いわく: ハンドラメソッドの自動呼び出し - -プログラミング経験の長い読者の中には、「ハンドラメソッドを定義しただけなのに、なぜTremaはこのメソッドを自動的にみつけられるんだろう?」と不思議に思った人がいるかもしれません。プログラム中にどういう関数があるか(=コンパイル時情報)をプログラム自身が知る(=実行時)ことはむずかしいからです。特にCではコンパイル時と実行時の間にはぶ厚いカーテンが引かれているので普通は無理です。 - -実は、Rubyにはイントロスペクション(リフレクションや自己反映計算とも呼ぶ)と呼ばれる機能があり、オブジェクトが自らの持つメソッドを実行時に調べることができます。たとえばPacket Inメッセージが到着したとき、コントローラはイントロスペクションして自分が@{packet_in}というメソッドを持っているかどうかを実行時に調べます。そしてもしみつかればそのメソッドを呼んであげるというわけです。この機能は@{Controller}クラスを継承したときに自動的にコントローラへと導入されます。 - -===[/column] - -これで「Hello Trema!」の説明はおしまいです。Tremaで作るコントローラは基本的にこの「Hello, Trema!」と同じ構成をしています。つまり、これをベースにいくつか必要なハンドラメソッドを追加していけば、より複雑で実践的なコントローラを作ることができます。 - -== Tremaのファイル構成 - -最後にTremaのファイル構成を見ておきましょう。Tremaをダウンロードすると、いくつかのファイルとディレクトリがあることがわかります。次に主要なものを挙げましょう。 - - * @{bin/}: 各種コマンドの本体が置かれるディレクトリ - * @{build.rb}: ビルドスクリプト - * @{cruise.rb}: すべてのテストコードを実行するテストスイート(Trema開発者向け) - * @{features/}: 受け入れテスト一式(Trema開発者向け。) - * @{ruby/}: Rubyライブラリのソースコード - * @{spec/}: Rubyのユニットテスト一式(Trema開発者向け) - * @{src/examples/}: サンプルアプリ - * @{src/lib/}: Cライブラリのソースコード - * @{tmp}: ログファイルやPIDファイルといった一時ファイルの置き場 - * @{trema}: @{trema}コマンド - * @{unittests/}: Cのユニットテスト一式(Trema開発者向け) - -この中でもTremaでコントローラを作りたい人が読むべきは、サンプルアプリ(@{[trema]/src/examples})です。 - -===[column] @{取間,とれま}先生いわく: Trema のテスト - -Tremaにはずいぶんたくさんのテストコードが付いていて、Trema 開発者がテストをとても重視していることがわかると思います。テストの実行頻度も徹底していて、開発者が新しいコードをコミットする度にすべてのテスト(@{cruise.rb} スクリプト)を自動的に実行することで、「いつダウンロードしても正しく動く」ことを保証しているのです。この手法をよく「継続的インテグレーション」と呼びます。 - -#@warn(テストランプと天井の蛍光灯がかぶって見づらいので、写真を撮り直し) -//image[ccrb][テストの実行結果を示すランプ][width=5cm] - -Tremaを壊さないために、1つおもしろい工夫があります。@{ccrb}はTrema開発者の机に置いてあるランプで、テストの実行結果をランプの色で視覚的にフィードバックします。テストがすべて通るとランプが緑色に光り、もしエラーが起こった場合には、ランプが赤く光り開発メンバー全員にメールが飛びます。これによって、万が一壊してしまっても必ず誰かが気付けるようにしています。 - -このしくみには、環境構築が手軽なCruiseControl.rb(@{http://cruisecontrolrb.thoughtworks.com/})と自作プラグインを使っています。 - -===[/column] - -== サンプルアプリ - -サンプルアプリ(@{[trema]/src/examples/})は簡単なOpenFlowアプリケーションをたくさん含んでおり、実際のAPIの使い方を調べるのに便利です。以下におもなサンプルアプリをまとめます(括弧内は@{[trema]/src/examples/}内のディレクトリ名)。このうちいくつかは続く章で詳しく説明していきます。 - -: こんにちはTrema(@{hello_trema}) - この章で説明した「Hello Trema!」と表示するだけのサンプル。これを@{trema run}コマンドで実行すれば、手っ取り早くTremaを試すことができる(Tremaを始めたばかりの初心者向け) - -: Packet In(@{packet_in}) - OpenFlowメッセージの中でも重要なPacket Inメッセージをハンドルするサンプル。OpenFlowメッセージハンドラの定義方法や、Packet Inメッセージの取り扱いの基本が学べる - -: スイッチの監視(@{switch_monitor}) - スイッチがコントローラへ接続したり逆に切断したときのイベントを捕捉するサンプル。複数のハンドラを使った少し複雑なコントローラの実装が学べる(@{switch_monitoring_tool}にて詳説) - -: OpenFlowメッセージのダンプ(@{dumper}) - コントローラが受け取るすべてのOpenFlowメッセージを文字列としてダンプするサンプル。さまざまなハンドラの書き方リファレンスとして役に立つ - -: スイッチ情報(@{switch_info}) - スイッチの詳細情報を要求するFeatures Requestメッセージをコントローラに送信し、スイッチから受信したスイッチ情報を出力するサンプル。コントローラからスイッチへOpenFlowメッセージを送る方法が学べる - -: リピータハブ(@{repeater_hub}) - いわゆるバカハブ(ダムハブ)の実装。重要なOpenFlowメッセージであるFlow ModとPacket Outの基本が学べる。@{tdd}では少し進んだ話題として、これを題材にコントローラのテスト駆動開発手法を学ぶ - -: ラーニングスイッチ(@{learning_switch}) - 普通のスイッチをエミュレートするサンプル。FDBなどスイッチの基本構成を学ぶことができる(@{learning_switch}で詳説) - -: トラフィックモニタ(@{traffic_monitor}) - ラーニングスイッチを拡張し、ユーザごとのトラフィックを測れるようにしたもの。フローに含まれる統計情報の利用例として役に立つ(@{traffic_monitor}にて詳説) - -: 複数スイッチ対応ラーニングスイッチ(@{multi_learning_switch}) - ラーニングスイッチの複数スイッチ版です。ラーニングスイッチとの違い、とくにスイッチごとに FDB を管理する部分に注目してください。 - -: シンプルルータ(@{simple_router}) - ルータの基本機能を実装したサンプル。ルータでのパケットの書き換えと転送、およびルーティングテーブルの実装などルータの基本が学べる(@{router_part1}および@{router_part2}で詳説) - -Trema にはたくさんのAPIがあり、上述したサンプルではまだまだすべてを紹介しきれていません。新しいサンプルアプリを作った人は、ぜひGitHubでpullリクエストを送ってください。あなたの名前がTremaプロジェクトの貢献者リスト(@{https://github.com/trema/trema/graphs/contributors})に載るかもしれません! - -== まとめ - -さて、これでTremaの基本はおしまいです。この章ではTremaをセットアップし、すべてのコントローラのテンプレートとなる「Hello, Trema!」コントローラを書きました。この章で学んだことを簡単にまとめてから、実践的なコントローラの開発に入っていくことにしましょう。 - - * TremaはRubyGemsまたはソースコードからビルドしてインストールできる - * コントローラは@{trema run}コマンドでコンパイル無しにすぐ実行できる - * コントローラはRubyのクラスとして定義し、@{Controller}クラスを継承することで必要なメソッドや機能が取り込まれる - * コントローラクラスに各種イベントに対応するハンドラを定義することでOpenFlowコントローラを実装できる。たとえば、起動イベントに対応するハンドラは@{start} - * Tremaのファイル構成と主なサンプル一覧 - -これで基礎は十分にできました。次の章では、いよいよ実用的なOpenFlowコントローラを書き実際にスイッチをつないでみます。 - -== 参考文献 - -Rubyプログラミングが初めてという人達のために、この章では入門に役立つサイトや本をいくつか紹介します。 - -: Why's (Poignant) Guide to Ruby(@{http://mislav.uniqpath.com/poignant-guide/}) - 私は大学や職場でいろいろなプログラミング言語を勉強してきましたが、これほど読んでいて楽しい本に出会ったことはありません。この本はRuby界の謎の人物_why氏による風変りなRuby入門で、プログラミング言語の解説書にもかかわらずまるで小説やマンガのようにリラックスして読めます。この章のRubyの品詞の説明は、この本を参考にしました。(日本語版はこちら@{http://www.aoky.net/articles/why_poignant_guide_to_ruby/})。 - -: TryRuby(@{http://tryruby.org/}) - 同じく_why氏によるブラウザで動くRuby環境です。Rubyを試してみたいけどインストールするのが面倒という人は、まずはここでRubyを試してみましょう。@{help}と打つと15分の短いRubyチュートリアルが始まります。 - -: プログラミングRuby第2版(Dave Thomas、Chad Fowler、Andrew Hunt著、田和勝、まつもとゆきひろ訳/オーム社) - Rubyの完全なリファレンスです。本気でRubyを勉強したい人は持っていて損はしません。この本だけあれば十分です。 diff --git a/en/openflow_frameworks.re b/en/openflow_frameworks.re deleted file mode 100644 index 3ec35c32..00000000 --- a/en/openflow_frameworks.re +++ /dev/null @@ -1,469 +0,0 @@ -= OpenFlow Development Framework - -//lead{ -Okay, so let's make something with OpenFlow already! But before that, let's take a look at some convenient OpenFlow development frameworks. Standing on the shoulders of giants will take you to your destination in a breeze. -//} - -//indepimage[robot][][width=10cm] - -== Let's make use of the development framework - -Nowadays, Web application frameworks such as Ruby on Rails@{rails> is indispensable when launching a new Web service. Without such framework's help, constructing the Web site from scratch will not only result in tremendous amount of codings but also in reinventing the wheel. You could easily find books on various frameworks for programmers who want to effectively create Web sites in book stores' technology section. So one can say that using a framework to build a Web service is already a common sense. - -//footnote[rails][@{http://rubyonrails.org/}] - -It's also a lot of work to build a OpenFlow controller from scratch. The standard specification of OpenFlow is written in C so it's essential that you can understand the language. After comprehending the specification, you have to write a library for the programming language that's used in the development, -construct the controller on top of that, and … it's quite a work just by thinking. On top of that, you would need to prepare some testing tools on your own as well. - -This is where the OpenFlow controller frameworks come in. They don't go as far as the ones in the Web industry, but there are already several OpenFlow controller frameworks for major programming languages ready to be used out there. Some of the frameworks provide convenient tools for developing and debugging as a part of the framework. There are no reason not to use them. - -Major OpenFlow controller frameworks are listed in @{frameworks}. They are all so-called open source software and the difference is the languages they are developed in. - -//table[frameworks][Major OpenFlow controller framework]{ -Name Language Developed by License ----------------------------------------------------------------------------------- -Trema Ruby Trema Project GPL2 -NOX C++ Nicira, Stanford University, UC Berkeley GPL3 -POX Python UC Berkeley GPL3 -Floodlight Java Big Switch Networks Inc. Apache -//} - -Now let's take a look at their details. - -== Trema - -Trema is an OpenFlow controller framework in Ruby(@{trema}) and it's a free software with GPL version 2 license. - -//image[trema][Trema Website (@{http://trema.github.com/trema})][width=12cm] - -The most distinctive feature of Trema is that it puts emphasis on the development efficiency than the execution speed, as you can see from the fact that it targets Ruby. For example, implementing a controller by using Trema drastically reduces the code length compared to other frameworks. @{trema_hub} is an example of a controller written by Trema and by only 14 lines, one can write a full controller that works as a hub. - -//list[trema_hub][Example of a controller (hub) written by Trema]{ -class RepeaterHub < Controller - def packet_in datapath_id, message - send_flow_mod_add( - datapath_id, - :match => ExactMatch.from( message ), - :actions => SendOutPort.new( OFPP_FLOOD ) - ) - send_packet_out( - datapath_id, - :packet_in => message, - :actions => SendOutPort.new( OFPP_FLOOD ) - ) - end -end -//} - -Trema has a plenty of tools that helps developing the controller, and one of the most powerful tool is the network emulator which is handy when testing the controller. This means that controllers can be developed with just a notebook PC and controllers can be implemented on arbitrary virtual environment by combining virtual switches and virtual hosts. Of course, controllers developed in this way works the same in the real network. - -== NOX - -NOX is the oldest framework developed in Stanford University where OpenFlow was born, using C++(@{nox}). It is a free software with GPL version 3 license. - -//image[nox][NOX Website (@{http://www.noxrepo.org/nox/about-nox/})][width=12cm] - -NOX is known for its large number of users. It's been developed since the birth of the OpenFlow and the key persons of SDN including the researchers who devised the OpenFlow specification are engaged in a lively discussion over the mailing list. In addition, the accumulated information over the past years assists developers in need. - -Below is a sample NOX code for implementing the same hub that was shown with Trema(@{nox_hub}). - -//list[nox_hub][Example of a controller (hub) written by NOX]{ -#include -#include -#include "assert.hh" -#include "component.hh" -#include "flow.hh" -#include "packet-in.hh" -#include "vlog.hh" - -#include "netinet++/ethernet.hh" - -namespace { - -using namespace vigil; -using namespace vigil::container; - -Vlog_module lg("hub"); - -class Hub - : public Component -{ -public: - Hub(const Context* c, - const json_object*) - : Component(c) { } - - void configure(const Configuration*) { - } - - Disposition handler(const Event& e) - { - const Packet_in_event& pi = assert_cast(e); - uint32_t buffer_id = pi.buffer_id; - Flow flow(pi.in_port, *(pi.get_buffer())); - - if (flow.dl_type == ethernet::LLDP){ - return CONTINUE; - } - - ofp_flow_mod* ofm; - size_t size = sizeof *ofm + sizeof(ofp_action_output); - boost::shared_array raw_of(new char[size]); - ofm = (ofp_flow_mod*) raw_of.get(); - - ofm->header.version = OFP_VERSION; - ofm->header.type = OFPT_FLOW_MOD; - ofm->header.length = htons(size); - ofm->match.wildcards = htonl(0); - ofm->match.in_port = htons(flow.in_port); - ofm->match.dl_vlan = flow.dl_vlan; - ofm->match.dl_vlan_pcp = flow.dl_vlan_pcp; - memcpy(ofm->match.dl_src, flow.dl_src.octet, sizeof ofm->match.dl_src); - memcpy(ofm->match.dl_dst, flow.dl_dst.octet, sizeof ofm->match.dl_dst); - ofm->match.dl_type = flow.dl_type; - ofm->match.nw_src = flow.nw_src; - ofm->match.nw_dst = flow.nw_dst; - ofm->match.nw_proto = flow.nw_proto; - ofm->match.tp_src = flow.tp_src; - ofm->match.tp_dst = flow.tp_dst; - ofm->cookie = htonl(0); - ofm->command = htons(OFPFC_ADD); - ofm->buffer_id = htonl(buffer_id); - ofm->idle_timeout = htons(5); - ofm->hard_timeout = htons(5); - ofm->priority = htons(OFP_DEFAULT_PRIORITY); - ofm->flags = htons(0); - ofp_action_output& action = *((ofp_action_output*)ofm->actions); - memset(&action, 0, sizeof(ofp_action_output)); - action.type = htons(OFPAT_OUTPUT); - action.len = htons(sizeof(ofp_action_output)); - action.port = htons(OFPP_FLOOD); - action.max_len = htons(0); - send_openflow_command(pi.datapath_id, &ofm->header, true); - free(ofm); - - if (buffer_id == UINT32_MAX) { - size_t data_len = pi.get_buffer()->size(); - size_t total_len = pi.total_len; - if (total_len == data_len) { - send_openflow_packet(pi.datapath_id, *pi.get_buffer(), - OFPP_FLOOD, pi.in_port, true); - } - } - - return CONTINUE; - } - - void install() - { - register_handler(boost::bind(&Hub::handler, this, _1)); - } -}; - -REGISTER_COMPONENT(container::Simple_component_factory, Hub); - -} -//} - -== POX - -POX is a project that was derived from NOX and a framework for developing controllers in Python(@{pox}). It's a free software with GPL version 3 license. - -//image[pox][POX Website (@{http://www.noxrepo.org/pox/about-pox/})][width=12cm] - -POX works on regardless of the OS whether it's Linux, Mac, or Windows since POX is implemented with Pure Python. There aren't a lot of sample application since it's rather a 'young' project but it's getting attentions from Python programmers. - -Below is a sample POX code for implementing the same hub (@{pox_hub}). - -//list[pox_hub][Example of a controller (hub) written by POX]{ -from pox.core import core -import pox.openflow.libopenflow_01 as of - -class RepeaterHub (object): - def __init__ (self, connection): - self.connection = connection - connection.addListeners(self) - - def send_packet (self, buffer_id, raw_data, out_port, in_port): - msg = of.ofp_packet_out() - msg.in_port = in_port - if buffer_id != -1 and buffer_id is not None: - msg.buffer_id = buffer_id - else: - if raw_data is None: - return - msg.data = raw_data - action = of.ofp_action_output(port = out_port) - msg.actions.append(action) - self.connection.send(msg) - - def act_like_hub (self, packet, packet_in): - self.send_packet(packet_in.buffer_id, packet_in.data, - of.OFPP_FLOOD, packet_in.in_port) - - def _handle_PacketIn (self, event): - packet = event.parsed - if not packet.parsed: - return - packet_in = event.ofp # The actual ofp_packet_in message. - self.act_like_hub(packet, packet_in) - -def launch (): - def start_switch (event): - RepeaterHub(event.connection) - core.openflow.addListenerByName("ConnectionUp", start_switch) -//} - -== Floodlight - -Floodlight is a framework in Java(@{floodlight}) with the Apache license. - -//image[floodlight][Floodlight Website (@{http://www.noxrepo.org/pox/about-pox/})][width=12cm] - -The attribute of Floodlight boils down to adopting Java which has a large population of programmers. Java is one of the first programming languages taught in universities these days so it should be easily accessible for most people. In addition, the implementation is in Pure Java so it works on regardless of the OS like POX. - -Below is a sample Floodlight code for implementing the same hub (@{floodlight_hub}). - -//list[floodlight_hub][Example of a controller (hub) written by Floodlight]{ -package net.floodlightcontroller.hub; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Map; -import net.floodlightcontroller.core.FloodlightContext; -import net.floodlightcontroller.core.IFloodlightProviderService; -import net.floodlightcontroller.core.IOFMessageListener; -import net.floodlightcontroller.core.IOFSwitch; -import net.floodlightcontroller.core.module.FloodlightModuleContext; -import net.floodlightcontroller.core.module.FloodlightModuleException; -import net.floodlightcontroller.core.module.IFloodlightModule; -import net.floodlightcontroller.core.module.IFloodlightService; -import org.openflow.protocol.OFMessage; -import org.openflow.protocol.OFPacketIn; -import org.openflow.protocol.OFPacketOut; -import org.openflow.protocol.OFPort; -import org.openflow.protocol.OFType; -import org.openflow.protocol.action.OFAction; -import org.openflow.protocol.action.OFActionOutput; -import org.openflow.util.U16; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class Hub implements IFloodlightModule, IOFMessageListener { - protected static Logger log = LoggerFactory.getLogger(Hub.class); - protected IFloodlightProviderService floodlightProvider; - - public void setFloodlightProvider(IFloodlightProviderService floodlightProvider) { - this.floodlightProvider = floodlightProvider; - } - - @Override - public String getName() { - return Hub.class.getPackage().getName(); - } - - public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) { - OFPacketIn pi = (OFPacketIn) msg; - OFPacketOut po = (OFPacketOut) floodlightProvider.getOFMessageFactory() - .getMessage(OFType.PACKET_OUT); - po.setBufferId(pi.getBufferId()) - .setInPort(pi.getInPort()); - - OFActionOutput action = new OFActionOutput() - .setPort((short) OFPort.OFPP_FLOOD.getValue()); - po.setActions(Collections.singletonList((OFAction)action)); - po.setActionsLength((short) OFActionOutput.MINIMUM_LENGTH); - - if (pi.getBufferId() == 0xffffffff) { - byte[] packetData = pi.getPacketData(); - po.setLength(U16.t(OFPacketOut.MINIMUM_LENGTH - + po.getActionsLength() + packetData.length)); - po.setPacketData(packetData); - } else { - po.setLength(U16.t(OFPacketOut.MINIMUM_LENGTH - + po.getActionsLength())); - } - try { - sw.write(po, cntx); - } catch (IOException e) { - log.error("Failure writing PacketOut", e); - } - - return Command.CONTINUE; - } - - @Override - public boolean isCallbackOrderingPrereq(OFType type, String name) { - return false; - } - - @Override - public boolean isCallbackOrderingPostreq(OFType type, String name) { - return false; - } - - @Override - public Collection> getModuleServices() { - return null; - } - - @Override - public Map, IFloodlightService> - getServiceImpls() { - return null; - } - - @Override - public Collection> - getModuleDependencies() { - Collection> l = - new ArrayList>(); - l.add(IFloodlightProviderService.class); - return l; - } - - @Override - public void init(FloodlightModuleContext context) - throws FloodlightModuleException { - floodlightProvider = - context.getServiceImpl(IFloodlightProviderService.class); - } - - @Override - public void startUp(FloodlightModuleContext context) { - floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this); - } -} -//} - -== Which one should you use? - -So, how does one choose a framework? A proper answer would be, "Use a framework that supports the language that the developers are used to". In other words, if you have a team of programmers who are Ruby users, Trema would be the only choice and for C++ programmers, NOX would be the answer. - -To back up this statement, there's a convincing data in the great book,『Code Complete: A Practical Handbook of Software Construction, Second Edition』@{codecomplete}. - -//footnote[codecomplete][Steve McConell/Microsoft Press] - -//quote{ -Programmers are more productive using a familiar language than an unfamiliar one. Data from the Cocomo II estimation model shows that programmers working in a language they've used for three years or more are about 30 percent more productive than programmers with equivalent experience who are new to a language (Boehm et al. 2000). An earlier study at IBM found that programmers who had extensive experience with a programming language were more than three times as productive as those with minimal experience (Walston and Felix 1977) -//} - -This is just a matter-of-fact-principle but it's often ignored in programming fields. People would say "Because I was told by my boss", "Everybody seems to be using it", and etc as a reason for choosing a framework only to find out that the project is on the death march. Programmers must use the programming language that they are familiar with. - -On the other hand, when a programmer is proficient in several languages, it's a fact that there's an apparent difference in productivity among the languages. The productivity and the quality is some ten times different between the low-level languages that need explicit memory management such as C and C++, languages that append garbage collection such as Java and C#, and high-level scripting languages such as Ruby and Python. Let us refer to 『Code Complete』 again. - -//quote{ -Programmers working with high-level languages achieve better productivity and quality than those working with lower-level languages. (...) You save time when you don't need to have an awards ceremony every time a C statement does what it's supposed to. Moreover, higher-level languages are more expressive than lower-level languages. Each line of code says more. -//} - -This is obvious when you compare the number of code lines for implementing the hubs(@{comparison}). - -//image[comparison][Comparison of number of code lines to implement hubs by major frameworks][width=12cm] - -//noindent -Frameworks that adopt scripting languages such as Trema (Ruby) and POX (Python) can be implemented with a small number of code lines but those with conventional languages such as NOX (C++) or Floodlight (Java) require much more lines. Especially for Floodlight, 111 lines are needed whereas only 14 lines suffice for Trema. It's hard to say the number code lines is directly proportional to the programming effort but Trema is about 1/8 of Floodlight. - -So, which framework would you choose? - -===[column] Mr. Torema says: What will become of OpenFlow controller development in the future? - -I would say the OpenFlow controller frameworks will walk the same path as that of the Web application. Looking at the history, frameworks for Java gained popularity between 1990s and beginning of 2000s. Countless frameworks for Java sprung up like mushrooms after rain and new technologies such as Java EE, JSP, and JSF have emerged. It was laborious to write Web applications that required programmers to understand new specifications one after another and at the same time, struggle with long codes that IDE automatically generates and the XML files. However in 2004, Web industry underwent a complete change by the appearance of Ruby Web application framework called Ruby on Rails. The concept of avoiding bulky implementation by Java and writing shortest possible codes took the Web industry by the storm. This mindset was introduced to Java's world of 'a framework with a long code' and gave birth to modernistic frameworks such as Django and Play. - -OpenFlow controller frameworks are still in its earliest days. There are frameworks that implements the recent frame of mind like Trema and POX but the mainstream frameworks such as NOX and Floodlight borrow conventional way of thinking. However, the overall productivity should increase manyfold if there are more programmers who can use scripting languages in the network industry. In addition, when the concept sinks in smoothly, there should be more frameworks with higher productivity in various languages. - -===[/column] - -== Other tools (Oflops) - -Oflops is a micro benchmark for OpenFlow controllers and switches. It provides Cbench (benchmark for controllers) and OFlops (benchmark for switches). There aren't a lot of chances to build switches so we'll be explaining on Cbench here. - -Cbench measures the number of Flow Mod that the controller can output per second. Cbench connects to the controller by acting as a switch and sends Packet In to the controller. The controller reacts to this and counts the number of Flow Mod, which counts as a 'score'. A controller with a high score is considered fast. - -Cbench supports two kinds of benchmarks. - -//noindent -@{Latency Mode} - - 1. Send Packet In to controller - 2. Wait for Flow Mod to return from the controller - 3. Repeat step 1 and 2 - -//noindent -@{Throughput Mode} - - 1. Send Packet In without waiting for Flow Mod - 2. Count when Flow Mod is received - -=== Example of Cbench (Trema) - -The above two benchmarks can be easily run in Trema since Trema consists of Cbench and the controller which can connect with the Cbench. The following command displays the result of latency mode and throughput mode. (Installation of Trema will be explained in the following @{openflow_framework_trema}) - -//cmd{ -% ./build.rb cbench -./trema run src/examples/cbench_switch/cbench-switch.rb -d -/home/yasuhito/play/trema/objects/oflops/bin/cbench --switches 1 --loops 10 --delay 1000 -cbench: controller benchmarking tool - running in mode 'latency' - connecting to controller at localhost:6633 - faking 1 switches :: 10 tests each; 1000 ms per test - with 100000 unique source MACs per switch - starting test with 1000 ms delay after features_reply - ignoring first 1 "warmup" and last 0 "cooldown" loops - debugging info is off -1 switches: fmods/sec: 10353 total = 10.352990 per ms -1 switches: fmods/sec: 10142 total = 10.141990 per ms -1 switches: fmods/sec: 10260 total = 10.259990 per ms -1 switches: fmods/sec: 10736 total = 10.734497 per ms -1 switches: fmods/sec: 10884 total = 10.883989 per ms -1 switches: fmods/sec: 10752 total = 10.751989 per ms -1 switches: fmods/sec: 10743 total = 10.742989 per ms -1 switches: fmods/sec: 10828 total = 10.827989 per ms -1 switches: fmods/sec: 10454 total = 10.453990 per ms -1 switches: fmods/sec: 10642 total = 10.641989 per ms -RESULT: 1 switches 9 tests min/max/avg/stdev = 10141.99/10883.99/10604.38/245.53 responses/s -./trema killall -./trema run src/examples/cbench_switch/cbench-switch.rb -d -/home/yasuhito/play/trema/objects/oflops/bin/cbench --switches 1 --loops 10 --delay 1000 --throughput -cbench: controller benchmarking tool - running in mode 'throughput' - connecting to controller at localhost:6633 - faking 1 switches :: 10 tests each; 1000 ms per test - with 100000 unique source MACs per switch - starting test with 1000 ms delay after features_reply - ignoring first 1 "warmup" and last 0 "cooldown" loops - debugging info is off -1 switches: fmods/sec: 36883 total = 36.761283 per ms -1 switches: fmods/sec: 36421 total = 36.398433 per ms -1 switches: fmods/sec: 37286 total = 37.174106 per ms -1 switches: fmods/sec: 36559 total = 36.526637 per ms -1 switches: fmods/sec: 36072 total = 36.007331 per ms -1 switches: fmods/sec: 34130 total = 33.993855 per ms -1 switches: fmods/sec: 32119 total = 32.086016 per ms -1 switches: fmods/sec: 33733 total = 33.533876 per ms -1 switches: fmods/sec: 33270 total = 33.262582 per ms -1 switches: fmods/sec: 32119 total = 32.107056 per ms -RESULT: 1 switches 9 tests min/max/avg/stdev = 32086.02/37174.11/34565.54/1866.96 responses/s -./trema killall -//} - -====[column] Mr. Torema says: Something you should be careful about Cbench - -Don't rely too much on the Cbench score. Currently, it seems that some OpenFlow controller frameworks vie for Cbench score only. For example, Floodlight claims that it can output million Flow Mods per second, which is quite impressive. This number probably can not be achieved without taking a full advantage of the thread and I/O ability. Anyway, it's amazing. Not that the number means anything. - -Think of a situation where Flow Mods have to be output million times per second. This also means Packet In occurs million times per second and what does that imply? There's some flow set in the switch but no packet matches it, and all the clueless packets arrive at the controller - this happens a million times per second. Don't you think something is wrong with this? - -The controller's ability to process Packet In is not important unless the ability is extremely low. As in data centers, where the location of the machines and the communication pattern is known, Packet In doesn't occur that often as long as the flows are properly designed. Designing a network and flow to avoid Packet In is far more important than processing Packet In with a feat of strength. - -If one misses to understand the reason behind a result of a measured object in Micro benchmark like Cbench, the conclusion can be blown all out of proportion. Please treat Cbench score as just a reference. - -====[/column] - -== Wrap-up - -In this section, we introduced some major OpenFlow controller frameworks. You can choose the framework that supports the language that suits your taste. - -If you want a framework with high productivity, you might want to use Trema or POX. In an ever-changing SDN industry, it's sometimes more important to prioritize productivity than the execution speed. - -In Part 2, we'll be learning OpenFlow programming using Trema. It starts with the basics of Ruby so even if you're a first-timer with the language, you'll be able to follow through. diff --git a/en/openflow_spec.re b/en/openflow_spec.re deleted file mode 100644 index a81091aa..00000000 --- a/en/openflow_spec.re +++ /dev/null @@ -1,312 +0,0 @@ -= Mechanism of OpenFlow - -//lead{ -Now that we acknowledge the concept of the OpenFlow, let's proceed to a bit more detailed specification. -We're going to put an emphasis on the practicality and grasp the points of the OpenFlow specification and frequently used words. -//} - -//indepimage[torema][][width=10cm] - -== Standard specification of OpenFlow - -OpenFlow's standard specification mainly defines the following two items. - - 1. Communication protocol between the controller and switch (secure channel) - 2. Switch's behavior on configured flow entry - -//noindent -In this section, we'll focus only on the more used function among the two. - -The latest version of the OpenFlow specification was 1.3 when this book was written, but the later descriptions are based on 1.0 since the corresponding commercial switches are also based on 1.0. There aren't radical changes on the idea or the model from 1.0 and above so if you can understand 1.0, it should be easy to understand the later versions as well. - -== Exchange between the switch and the controller - -OpenFlow switches and controllers operate by exchanging messages defined by the OpenFlow specification. Here we explain what kind of messages are sent and received in a sequential order. Recall the story of the customer support center in @{whats_openflow} when you read. - -=== Establish connection between the switch and the controller - -The first thing that the controller and switch do is to establish the secure channel connection. There aren't any rules on which one should initiate the connection first but it seems to be from the switch to the controller in most of the recent implementations as shown in @{secure_channel}. - -//image[secure_channel][Switch connects to the controller and establishes secure channel][width=12cm] - -Secure channel is a regular TCP connection. When both the controller and the switch are OpenFlow compatible, a more secure TLS (Transport Layer Security) can also be used although the performance may marginally degrade. - -=== Negotiate version - -The next step is confirming the OpenFlow protocol version, the so-called version negotiation. After establishing the secure channel, the switch and the controller exchange Hello messages with their own version numbers (@{version_negotiation}). - -//image[version_negotiation][Confirming the other side's OpenFlow protocol version by exchanging the Hello message][width=12cm] - -The negotiation is successful if the same version is spoken, and then the real communication begins. - -=== Confirm switch's specification - -Next, the controller confirms the connected switch's specification. The controller sends a 'Features Request' message to the switch and asks for the following information since only the version can be confirmed right after the negotiation. - - * Unique ID of switch (Data path ID) - * List of physical ports - * List of supported functions - -//image[features_request_reply][Confirm switch specification by 'Features Request' message][width=12cm] - -The switch replies by sending 'Features Reply' message. - -=== Receive unknown packets - -When the switch detects communication that is not registered in the flow table, it notifies the controller of that packet and related information. This is called 'Packet In' message. - -//image[packet_in][Unknown packet and related information is sent to the controller as 'Packet In' message][width=12cm] - -=== Update flow table and forward packet - -When forwarding the packets, a flow entry is written to the flow table in the switch, to forward the packets by 'Flow Mod' message. Then the packets that caused 'Packet In' are sent to the appropriate destination by 'Packet Out' message. Without this, the data of the packets that evoked 'Packet In' ends up remaining in the controller, never to be sent to the destination (@{flowmod_packetout}). - -//image[flowmod_packetout][Update the flow table by 'Flow Mod' and forward packets that caused 'Packet In' by 'Packet Out'][width=12cm] - -====[column] Mr. Torema says: 'Flow Mod' and 'Packet Out' at the same time? - -Actually, there's a way to do 'Flow Mod' and 'Packet Out' at the same time. Although it's written in the OpenFlow specification, it's rather a dangerous programming style. - -When the switch receives 'Packet In', the switch buffers the contents of the packet that evoked 'Packet In' and the ID of the buffer (Buffer ID) is notified to the 'Packet In' message that is sent to the controller. If this is set at the timing of 'Flow Mod', the switch performs 'Packet Out' (@{buffer_id}). - -//image[buffer_id]['Flow Mod' and 'Packet Out' at the same time by designating Buffer ID][width=12cm] - -However, this is a @{taboo}. Here's why. - - * It's hit or miss whether the packets designated by the Buffer ID are still in the switch's buffer or not since there's no way to observe from outside of the switch - * Even if you know for sure that the packets are still in the switch's buffer, they might disappear as soon as you type in 'Flow Mod' - * Cheap switches might not have a buffer from the beginning - -So it's safe to send 'Packet Out' and 'Flow Mod' commands independently as explained in this book. - -Just for the record when 'Flow Mod' and 'Packet Out' is done at the same time, here's a pseudo code that works even if there's nothing in the buffer. - -//emlist{ -begin - flow_mod( Buffer-ID = packet_in.buffer_id ) # Designation of Buffer ID -rescue - packet_out( packet_in ) # If Flow Mod fails, explicitly Packet Out -end -//} - -//noindent -As you can see, you have to write 'Packet Out' as an exception handling in case there's nothing in the buffer. The code gets longer as a result. - -Let's look at the correct way: - -//emlist{ -flow_mod # No designation of Buffer ID -packet_out( packet_in ) -//} - -//noindent -It's much shorter this way and it works just right as well. - -====[/column] - -=== Lifespan of flow table and statistical information - -A 'lifespan' can be set to the flow entry that is created by 'Flow Mod'. There are two types of lifespan. - - * Idle time out: When the non-referred time reaches this lifespan, the flow entry is deleted. Once the packets arrive and the flow entry is referred, the time is reset to 0 second. - * Hard time out: The flow entry is deleted when this lifespan is reached (counted from the moment when the flow entry was written) regardless of the referred status. - -When these time outs are set to 0, the flow entry remains in the flow table unless it is explicitly deleted. - -When the flow entry is deleted, the information of the deleted flow entry and the statistical information of the packets processed according to that flow entry are notified to the controller. This is called 'Flow Removed' message and it's used for collecting the network traffic. - -//image[flow_removed][When the flow entry is deleted by time out, the statistical information of the forwarded packet is sent to the controller as 'Flow Removed'][width=12cm] - -== Inside the flow entry - -A flow entry is composed of the following 3 components as shown in @{whats_openflow}. - - * Matching rule - * Action - * Statistical information - -We'll be looking at these components in detail but you don't have to remember everything from the beginning. Please use the following subsections as a reference when you get lost in the following chapters. - -=== Matching rule - -Matching rule is a condition that makes the OpenFlow switch to decide whether it should take an action or not when it receives packets. For example, the switch takes an action only on the packets that match the given rules such as 'If the packet's destination is a http server' or 'If the packet's destination is a broadcast address'. - -12 types of rules (@
{matching_rules}) can be used in OpenFlow 1.0. These rules are values well used in ethernet and TCP/UDP. - -====[column] Mr. Torema says: Another name of matching rule - -There're actually other ways to call 'matching rule': 'OpenFlow 12 tuple' and 'header field'. Which is quite confusing so it's just 'matching rule' in this book. It's because the name expresses the role of 'When packet arrives, match according to the rules' straightforwardly and the most easily comprehensible. - -"OpenFlow 12 tuples" becomes 15 tuples in OpenFlow 1.1, and who knows how many there'll be in the future? In other words, on one knows till when this name is available. Also the name 'header field' doesn't really tell what it does and seems a bit difficult in a strange way. - - -====[/column] - -//table[matching_rules][12 kinds of conditions that can be set by matching rule]{ -Name Description --------------------------------------------------------------- -Ingress Port Physical port number of switch -Ether src Source MAC address -Ether dst Destination MAC address -Ether type Ethernet type -IP src Source IP address -IP dst Destination IP address -IP proto IP protocol type -IP ToS bits IP ToS information -TCP/UDP src port Source port number of TCP/UDP -TCP/UDP dst port Destination port number of TCP/UDP -VLAN id VLAN ID -VLAN priority VLAN PCP value (CoS) -//} - -In the world of the OpenFlow, the communication is controlled by freely combining the conditions that can be set by these matching rules. Example conditions follow. - - * Rewrite packet that arrives at switch's physical port #1 with destination of TCP 80 (= HTTP) - * Deny packet with MAC address of 02:27:e4:fd:a3:5d and destination IP address of 192.168.0.0/24 - -These are called 'Wildcard Match' since only partial conditions are specified. Of course, it's possible to create a matching rule that specifies all 12 types of conditions, which is called 'Exact Match'.'Exact Match' is used when a particular packet needs to be matched by the flow entry and 'Wildcard Match' is used when a wide range of types of packets needs to matched by a single flow entry. - -====[column] Mr. Torema says: OSI network model falls apart? - -A lad with a rich experience in network once said, - -'If OpenFlow can do anything across the layers, wouldn't OSI network model@{OSImodel} fall apart?' - -//footnote[OSImodel][It's those Layer 2 and Layer 3 stuff. To be more correct, it's a construction policy of the network architecture defined by ISO to realize data communication between dissimilar devices.] - -There's no need for such worries. OSI network model is a 'OSI reference model' and it's a model to 'reference' for grouping the communication protocol and make them look better. For example, consider a situation that you've made a protocol called xyz and have to explain it to someone. You would start the sentence by pointing (referencing) to a layer such as 'This is a Layer 3 protocol and…' which would help the listener understand you better. In other words, the OSI network model is conveniently used as a vocabulary that's understood between the people who know a little bit about the network. -However, this is nothing more than a reference let alone a policy, so it doesn't mean that every network protocol and networking device have to abide the rule. As said before, a sentence like 'If abc should be in the OSI, it would be placed in Layer 4' would suffice. - -So, it's just that the OpenFlow happens to use a couple of layers' information. - -====[/column] - -=== Action - -An action is something like a @{verb} that specifies how to cook the packet that arrived at the switch. -The phrase such as 'Rewrite the packet by OpenFlow and forward' is often used and this rewriting process can be realized by the actions. So let's take a look at what kind of actions are defined in OpenFlow 1.0. - -There are 4 main types of actions. - - * Forward: Forward the packet from the designated port - * Modify-Field: Rewrite the content of the packet - * Drop: Drop packets - * Enqueue: Attach packet to the designated switch for each port (for QoS) - -Like a verb, the action is performed sequentially as commanded - 'Make onigiri, eat, and clean up'. For example, if packets need to be rewritten and outputted from the designated port, the list of actions should be something like this. - -//emlist{ -[Modify-Field, Forward] -//} - -Here, note that the actions are performed sequentially as commanded. If these orders are changed, you would get a different result. For example, if it's 'Eat onigiri and then make onigiri', you would have an onigiri left in the end. So if you reverse the above example, packets would be forwarded first. Even if the 'Modify-Field' happens afterwards, the packet would end up being destroyed after it's rewritten. - -//emlist{ -# Forwarding happens first before the packet is rewritten. -[Forward, Modify-Field] -//} - -Identical verbs can be placed multiple times. - -//emlist{ -[Modify-Field A, Modify-Field B, Forward A, Forward B] -//} - -The above example would be read as 'Rewrite field A and B, and forward it to port A and B'. Like this, a list of multiple actions@{num_actions} can be created when rewriting multiple fields or outputting packets to multiple ports. - -//footnote[num_actions][The maximum number of actions is dependent on the implementation of OpenFlow switch and controller. Normally the numbers wouldn't cause any problems.] - -Drop is a special kind of action and it's not really specifically defined. In case there aren't any Forward actions in the action list, the packets aren't forwarded anywhere but discarded. Therefore, it's conveniently called a Drop action. - -Now let's take a look at the most widely used Forward and Modify-Field action, and what they're capable of doing in detail. - -==== Forward action - -Forward action output packets from the designated port. Port numbers can be designated but logical ports, defined for the special purposes, can be used as well. - - * Port Number: Output packets from the designated port number - * IN_PORT: Output packets to ingress port - * ALL: Output packets to all ports except the ingress port - * FLOOD: Output packets according to the spanning tree that switch made - * CONTROLLER: Explicitly send packets to the controller and trigger Packet In - * NORMAL: Forward packets using switch's function - * LOCAL: Sends packets to the switch's local stack. Used when there's a need to hand the packets over to the application that runs on the local stack. Not used very often. - -Among these actions, FLOOD and NORMAL are logical ports when the function of OpenFlow and conventional switches is used together. - -==== Modify-Field action - -Modify-Field action rewrites various parts of the packet. - - * Rewrite source MAC address - * Rewrite destination MAC address - * Rewrite source IP address - * Rewrite destination IP address - * Rewrite ToS field - * Rewrite TCP/UDP source port - * Rewrite TCP/UDP destination port - * Delete VLAN header - * Rewrite VLAN ID (If no VLAN header exists, grant a new one) - * Rewrite VLAN priority (If no VLAN header exists, grant a new one) - -Let's look at what these actions can do and their typical usage. - -===== Rewrite MAC address - -A typical example of rewriting MAC address is the router. OpenFlow supports the action of rewriting source and destination MAC address that is necessary for implementing the router function. - -//image[rewrite_mac][Rewriting source and destination MAC address in router][width=12cm] - -A router works between two different networks and controls the traffic of the packets that come and go. When Host A sends a packet to the Host B residing in another network, the router receives the packet and determines where to forward it based on the destination IP address. Then the router rewrites the destination MAC address of the packet with the host MAC address of where the router is supposed to send the packet to. In addition, the router rewrites the source with the its own MAC address before forwarding the data. - -===== Rewrite IP address - -A well-known example of rewriting the IP address is the NAT (Network Address Translation). OpenFlow supports the action of rewriting source and destination IP address that is necessary for implementing the NAT function. - -//image[rewrite_ip_address][Rewriting source and destination IP address in NAT][width=12cm] - -Routers connected to the Internet rewrites the IP address to enable the communication between a private and a global network. -When a client in the private network wants to communicate with a server in the Internet, the gateway sends the packet after rewriting the source IP address of the packet that arrived from the private network -with the gateway's global IP address. The reply from the server to the client works the same way, only by rewriting the packet in vice-versa. - -===== Rewrite ToS field - -ToS field is used when a router needs to designate the processing priority of the received packets to control the communication QoS. OpenFlow supports this action of rewriting ToS field. - -===== Rewrite TCP/UDP port number - -A typical example of rewriting TCP/UDP port is the IP masquerade. OpenFlow supports the action of rewriting source and destination TCP/UDP port that is necessary for implementing the IP masquerading function. - -//image[rewrite_port][Rewriting source and destination TCP/UDP port in IP masquerade][width=12cm] - -In an environment such as broadband routers where multiple hosts simultaneously communicate with a single global address, TCP/UDP port numbers may be redundant with just the NAT. IP masquerading solves this issue by assigning the private network's port number to each host and translating the port numbers for each communication. - -===== Rewrite VLAN header - -Rewriting VLAN header is used for the special purpose of connecting the traditional network constructed with tagged VLANs and the network constructed with OpenFlow. VLAN divides the network constructed with the conventional switches (network within a broadcast's reach) into multiple networks and sometimes the divided network itself is called VLAN. It's the VLAN's tag (VLAN ID) that distinguishes each VLAN and the VLAN header that's assigned to the packet contains this tag information. There are three kinds of Modify-Field actions that is required for handling the VLAN header. - -//image[strip_vlan][Usage of rewriting the VLAN header action][width=12cm] - -: Delete VLAN header - An action that puts the packet with the VLAN header back to a regular packet by eliminating the VLAN header from the packet that flows in the VLAN. - -: Rewrite VLAN ID - Rewrite the VLAN ID of the VLAN packet. For example one can set an action that changes VLAN ID to 3. In addition, one can assign a VLAN header with a VLAN ID to a packet without a VLAN header. - -: Rewrite VLAN priority - Change the packet forwarding priority on VLAN. This priority is used when the traffic types (data, voice, video, and etc.) are specified and the value ranges from 0 (lowest) to 7 (highest). - -=== Statistical information - -In OpenFlow 1.0, below statistical information can be acquired per flow entry. - - * Number of received packets - * Number of received bytes - * Elapsed time since the flow entry was created (second) - * Elapsed time since the flow entry was created (nano second) - -== Wrap-up - -We took a look at the nuts and bolts in the OpenFlow specification. You can probably call yourself a OpenFlow specialist just by having learned all the things in the section. Let us introduce some popular programming frameworks for developing OpenFlow controller in the next section. - - diff --git a/en/openflow_usecases.re b/en/openflow_usecases.re deleted file mode 100644 index 3b2d00a6..00000000 --- a/en/openflow_usecases.re +++ /dev/null @@ -1,127 +0,0 @@ -= OpenFlow use case - -//lead{ -OpenFlow seems all-purpose at a glance, but is that true? Let's think about the situations where it would fit perfectly well. -//} - -//indepimage[pipeline][][width=10cm] - -== Academia-born OpenFlow - -OpenFlow switches can turn into any kind of networking devices as long as there's a programmed controller. Roughly speaking, as long as the controller is implemented, the OpenFlow switches can be anything from simple devices such as regular switches, to a bit more complicated equipments such as routers, load balancers, firewalls, and NATs. Of course, not all feature of the dedicated machines can be implemented and the performance might degrade with a poor implementation since parts of the features are implemented as a software. However, it still doesn't change the fact that anything is possible depending on the software. - -This 'anything is possible' feature was originally born by the needs of academic background such as universities and laboratories. 'I want to do a research on a whole new network, free from the limitations of the conventional switches and routers. But remodeling the firmware of switches and routers sounds too hard. I don't want to make a new hardware from the scratch… A large-scaled virtual network is okay for a testbed but it's too different from the real Internet'. OpenFlow was devised to solve these kinds of dilemmas. - -== Why is OpenFlow attracting attention? - -So, why did OpenFlow -- originally used for researching Internet -- start attracting attention from the industries represented by the enormous data centers? - -Two things that matter the most to data centers are throughput (processing ability per a unit time) and cost. Gigantic data centers such as Google need to equip a lot of switches and servers in order to process the endless requests from all over the world in a short time, i.e., to achieve a higher throughput. If these equipments are expensive, the total cost would be humongous just by the hardware expense. Therefore, they use an inexpensive hardware assembled with the commodity components that even you can get from the Akihabara (famous electronics district in Tokyo). - -The reliability of data centers is guaranteed by the software rather than the hardware. Since massive amounts of servers and switches are used in large-scaled data centers, the reliability of individual hardwares is ignored. In an environment where there are hundreds of thousands of servers, no one expects all those machines to keep working without any problems in the first place. Instead, the reliability is guaranteed from the upper software layer. Everything that's related to making the system trustworthy -- monitoring, assuring redundancy, managing faults, and recovering automatically -- is implemented as a middleware. - -OpenFlow fits well to this data center model. It decouples the software and the hardware; controller part that does the controlling and the OpenFlow switch part that only follows the instructions. This is similar to the data center model; a middleware guaranteeing the reliability by controlling the whole system, and a lot of commodity hardwares that are controlled. The collaboration with data center's middleware -- which guarantees the reliability by monitoring, assuring redundancy, recovering automatically -- is convenient since the controller is implemented as a software. It's more cost efficient and highly reliable than network and application individually guaranteeing the reliability. - -Above all, data center is a platform where new technologies such as OpenFlow can be easily adopted. Data centers are expanded in large units by the floors or buildings rather than by the servers. Hence, a unique new technology can be introduced without having to worry about the mutual connectivity with the conventional system. - -As it happens, this OpenFlow's architecture of 'the software controller and the hardware is nicely decoupled and the collaboration with the conventional middleware is convenient' matches that of the data center. This is why OpenFlow is getting attention. - -== All that use cases - -Here we'll be covering some of the ways to implement the fundamental networking devices such as switches and routers. But before we go on, let's look at what OpenFlow can do in more detail. - -Rephrasing the question 'What can I do with the OpenFlow?' would be 'What can I do with the flow table?'. OpenFlow switch forwarding packets one after another according to the flow table remind the authors of the Waterworks game that we used to play in the grade school. It's a game where you lay out cards of water pipes in different shapes to deliver the water from the valve to the tap. What you can do with the flow table well resembles this game. The four basic functions follow. - - 1. Forward packets - 2. Check flow rate - 3. Rewrite - 4. Branch - -This is everything. Forward the packets by outputting the packet from the designated switch port. Check the flow rate of the forwarded packets. Rewrite the packets. Replicate the packets and output them from the switch ports. By combining these functions freely, you can create various types of networks. - -Let us finally move on to the actual use cases. Notice what kind of combination with the above 4 cards is used to realize each case. - -=== Switch - -Using 'forward packets' card lets you realize the simplest switch with the OpenFlow (@{switch}). The switch checks the destination MAC address of the received packet and forwards the packet to the port where the host with the MAC address is connected. - -//image[switch][Realize switch with OpenFlow][width=8cm] - -If 'check flow rate' card is combined, traffic aggregating function is appended to the switch (@{traffic_switch}). The controller can summarize the whole network's traffic by adding up how many packets were forwarded for each flow table. - -//image[traffic_switch][Realize the switch with traffic aggregating function by OpenFlow][width=8cm] - -The details on how to implement the switch with the OpenFlow and the traffic aggregating function will be explained in @{learning_switch} and @{traffic_monitor}, respectively. - -=== Router - -Routers can be realized with OpenFlow when 'forward packets' and 'rewrite' cards are combined (@{router}). Routers operate between two different networks, forwarding and rewriting packets so that the two networks can communicate. When the packet goes through the router, the router rewrites the packet's destination and source MAC address before forwarding the packet. - -//image[router][Realize router with OpenFlow][width=12cm] - -The details on how to implement the router with OpenFlow will be explained in @{router_part1} and @{router_part2} - -=== Load balancer - -The so called load balancer can be realized by the OpenFlow if 'check flow rate' is appended to the router (@{load_balancer}). A load balancer is a device or a software that distributes the accesses from clients to multiple backend servers, to lower the load of servers with high access rates such as web servers. - -//image[load_balancer][Realize load balancer with OpenFlow][width=10cm] - -The operations of load balancer follow. - - 1. When a request from a client arrives, determine the backend server in charge - 2. Rewrite the request packet so that it can reach the backend server (same as the router) - 3. Output the rewritten packet from the switch port that the backend server is connected to - -//noindent -The load between the backend servers may not be balanced depending on the access situation. Using 'check flow rate' card, the backend with a lower load is chosen with priority. - -The number of necessary backend servers depends on the period of time. For example, the number of backend servers can be reduced in the middle of the night when access rate decrease. On the contrary, it must be increased around after lunch time when many people tend to surf the net. - -If there's an API that adjusts the number of the backend servers, the load balancing function and adjusting the number of the backend servers can be collaborated (@{advanced_load_balancer}). This is because the traffic can be examined by 'check flow rate' and accordingly, the number of the backend servers can be automatically adjusted through the API. - -//image[advanced_load_balancer][Example of high-spec load balancer that adjusts the number of backend servers according to the traffic amount][width=10cm] - -As shown above, the controller can append functions to the network by collaborating with conventional middleware such as backend server. The controller collaborates easily with various kinds of conventional middleware through the APIs since the controller can be implemented by major programming languages which will be introduced in @{openflow_frameworks}. - -We've introduced several examples on how to implement some basic networking devices with the OpenFlow so far. From now on, we'll be digging deeper into the patterns of how to control the networking route by the OpenFlow. - -=== Fully utilize the bandwidth - -If you use 'forward packet', large data such as VM images can be effectively forwarded by using multiple routes. Multiple connections from the source to the destination are made, and the connections are used simultaneously to forward the data (@{maximize_bandwidth}). - -//image[maximize_bandwidth][Earn bandwidth by using multiple routes][width=12cm] - -A detailed example of this 'fully utilize the bandwidth' will be introduced in @{google}. - -=== Packet replication - -When 'branch' is used, the packets sent from the server are copied at the switch and delivered to multiple clients (@{multicast}). Even if the number of clients increase, the switches will replicate the packets so there's no need to multiply the number of packets from the server. As a result, the network bandwidth can be saved. - -//image[multicast][Replicate packets and deliver them to multiple clients][width=10cm] - -In addition, the 'branch' will allow you to create redundant routes. If replicated packets are sent to each route, the network can be recovered without loosing a single packet in case of a failure (@{fail_over}). Think of a situation where a client and a server communicate. The left-most switch sends the replicated packets to both of the routes. The receiver accepts packets from only one of the routes. That way, even when there's a failure in one route, none of the packet will get lost since the other route is still working. - -//image[fail_over][Create redundant route with OpenFlow][width=12cm] - -What's important here is that the controller sees the whole picture and can make decision on routes at will. In the conventional network, each switch could only select a specific route, such as the shortest one, based on its individually calculated optimal route. However in the OpenFlow, the controller can change the route in any way and in any time it wants based on the overall knowledge on the route and the traffic situation. - -=== Create and change the network structure as you want - -By using the OpenFlow, you can change the network structure freely without being limited by the physical structure. Let's say two hosts and two networks are connected to a switch as shown in @{patch_panel}. -The controller can select the connection of hosts and networks freely by inserting a flow entry that says 'forward packet'. For example, Host A belongs to the Network B by mutually forwarding the packet between Host A's port and Network B's port. - -//image[patch_panel][Select the network where each host belongs][width=12cm] - -The hardware with this kind of function is called a patch panel and we'll be covering how to implement it with the OpenFlow in more details in @{patch_panel}. A more advanced example, a network virtualization so to speak, will be introduced in @{sliceable_switch} and @{datacenter_wakame}. - -== Wrap-up - -We looked at some specific use cases where the OpenFlow fits perfectly. You can add any kind of functions you want depending on the programming in the OpenFlow but it's not really realistic to implement every functions of the dedicated networking devices. Instead, it matches well with the world where the software controls the hardware, represented by the data centers. Implement the necessary functions for automation/optimization of network operation and achieve higher throughputs with the OpenFlow, while leaving the parts related to the reliability to other middleware. That's how OpenFlow should be used to display its great abilities. - -In the next section, we'll be introducing the specification of the OpenFlow in detail. - -== Reference - -: The Datacenter as a Computer: An Introduction to the Design of Warehouse-Scale Machines (Luiz Andre Barroso, Urs Holzle/Morgan and Claypool Publishers) - Google's enormous data center is called 'warehouse scale computer (WSC)' and the software as a computer controls tens of thousands servers squeezed into the gigantic warehouse. The book discusses the overall architecture of the WSC and the designs of middleware, based on an affluent data that only the actual data center operators could know. diff --git a/en/sty/jumoline.sty b/en/sty/jumoline.sty deleted file mode 100755 index 3b73ccfd..00000000 --- a/en/sty/jumoline.sty +++ /dev/null @@ -1,306 +0,0 @@ -%% -%% This is file `jumoline.sty', -%% generated with the docstrip utility. -%% -%% The original source files were: -%% -%% jumoline.dtx (with options: `package') -%% -%% IMPORTANT NOTICE: -%% -%% For the copyright see the source file. -%% -%% You are *not* allowed to modify this file. -%% -%% You are *not* allowed to distribute this file. -%% For distribution of the original source see the terms -%% for copying and modification in the file jumoline.dtx. -%% -%% Style file `jumoline'. -%% Copyright (C) 1999-2001 Hiroshi Nakashima -%% (Toyohashi Univ. of Tech.) -%% -%% This program can be redistributed and/or modified under the terms -%% of the LaTeX Project Public License distributed from CTAN -%% archives in directory macros/latex/base/lppl.txt; either -%% version 1 of the License, or any later version. -%% -%% \CharacterTable -%% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z -%% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z -%% Digits \0\1\2\3\4\5\6\7\8\9 -%% Exclamation \! Double quote \" Hash (number) \# -%% Dollar \$ Percent \% Ampersand \& -%% Acute accent \' Left paren \( Right paren \) -%% Asterisk \* Plus \+ Comma \, -%% Minus \- Point \. Solidus \/ -%% Colon \: Semicolon \; Less than \< -%% Equals \= Greater than \> Question mark \? -%% Commercial at \@ Left bracket \[ Backslash \\ -%% Right bracket \] Circumflex \^ Underscore \_ -%% Grave accent \` Left brace \{ Vertical bar \| -%% Right brace \} Tilde \~} -%% -%% -\def\next{LaTeX2e} -\ifx\fmtname\next -\def\next{ -\NeedsTeXFormat{LaTeX2e}[1994/12/01] -\ProvidesPackage{jumoline}} -\else\def\next[#1]{}\fi -\next -[2001/05/31 v1.2 ] - -\ifx\PackageError\undefined -\def\PackageError#1#2#3{\@latexerr{#1:#2}{#3^^J\@ehc}} -\fi - -%%^L - -%% Register Declaration - -\newdimen\UnderlineDepth \UnderlineDepth-\maxdimen -\newdimen\MidlineHeight \MidlineHeight-\maxdimen -\newdimen\OverlineHeight \OverlineHeight-\maxdimen -\newdimen\UMOlineThickness \UMOlineThickness.4pt - -\newdimen\UMO@height \newdimen\UMO@depth -\newdimen\UMO@dqspace \newdimen\UMO@tempdim - -\newskip\UMO@prejfmglue \newskip\UMO@postjfmglue - -\newcount\UMO@mode -\let\UMO@afterblock\z@ -\let\UMO@afterword\@ne -\let\UMO@afterchar\tw@ - -\newcount\UMO@spacefactor -\newcount\UMO@firstxspcode -\newcount\UMO@lastxspcode -\newcount\UMO@inhibitxspcode -\newcount\UMO@prebreakpenalty -\newcount\UMO@postbreakpenalty -\newcount\UMO@kpostbreakpenalty - -\newif\ifUMO@nospace \newif\ifUMO@firstelem - -%%^L - -%% User Interface and Initialization - -\def\Underline{\UMO@line\UnderlineDepth{-\UnderlineDepth}{-\dp\strutbox}} -\def\Midline{\setbox\@tempboxa\hbox{$B$"(B}% - \UMO@line\MidlineHeight\MidlineHeight{.5\ht\@tempboxa}} -\def\Overline{\UMO@line\OverlineHeight\OverlineHeight{\ht\strutbox}} -\def\UMOline{\UMO@line{-\maxdimen}\z@} - -\def\UMO@line#1#2#3#4{\begingroup \let\\\UMOnewline - \relax\ifdim#1<\z@ \UMO@height#3\relax - \else \UMO@height#2\relax \fi - \UMO@depth-\UMO@height - \ifdim\UMO@height<\z@ \advance\UMO@depth\UMOlineThickness - \else \advance\UMO@height\UMOlineThickness \fi - \settowidth\UMO@dqspace{$B!H(B}\advance\UMO@dqspace-1zw - \UMO@dqspace-\UMO@dqspace \divide\UMO@dqspace\tw@ - \UMO@nospacetrue \UMO@firstelemtrue - \UMO@mode\UMO@afterblock - \ifvmode\leavevmode\fi - \def\@tempa{#4 }\edef\@tempb{\noexpand\@nil\space}% - \expandafter\expandafter\expandafter\UMO@wordloop - \expandafter\@tempa\@tempb - \endgroup \UMO@aftergroup} - -%%^L - -%% Processing Word Elements - -\def\UMO@wordloop{\UMO@ifbgroup\UMO@wordblock\UMO@iwordloop} -\def\UMO@ifbgroup#1#2{\let\@tempa#1\let\@tempb#2\futurelet\@tempc\UMO@ifnc} -\def\UMO@ifnc{\ifx\@tempc\bgroup \let\next\@tempa \else\let\next\@tempb \fi - \next} -\def\UMO@wordblock#1{\UMO@spaceskip - \UMO@putbox\relax{#1}\UMO@nospacetrue \UMO@mode\UMO@afterblock - \UMO@spacefactor\@m \UMO@wordloop} -\def\UMO@iwordloop#1 {\def\@tempa{#1}\ifx\@tempa\@nnil \let\next\UMO@end - \else - \ifx\@tempa\empty \UMO@nospacefalse - \else - \UMO@spaceskip \UMO@mode\UMO@afterblock - \def\UMO@theword{}\UMO@firstxspcode\m@ne - \UMO@charloop#1\@nil \fi - \let\next\UMO@wordloop \fi - \next} - -%%^L - -%% Interword Spacing - -\def\UMO@spaceskip{\ifUMO@nospace \UMO@nospacefalse \else - \ifdim\spaceskip=\z@ - \@tempdima\fontdimen3\font\relax - \multiply\@tempdima\UMO@spacefactor \divide\@tempdima\@m - \@tempdimb\fontdimen4\font\relax \multiply\@tempdimb\@m - \divide\@tempdimb\UMO@spacefactor - \@tempskipa\fontdimen2\font plus\@tempdima minus\@tempdimb\relax - \else - \edef\@tempa{\the\spaceskip\space @ @ @ @ }% - \expandafter\UMO@setspaceskip\@tempa\@nil - \fi - \ifnum\UMO@spacefactor<2000\else - \ifdim\xspaceskip=\z@ \advance\@tempskipa\fontdimen7\font - \else \@tempskipa\xspaceskip - \fi\fi - \UMO@skip\@tempskipa \fi} -\def\UMO@setspaceskip#1 #2 #3 #4 #5 #6\@nil{\@tempdima\z@ \@tempdimb\z@ - \def\@tempa{#2}\def\@tempb{#3}% - \ifx\@tempa\UMO@plus \@tempdima#3\def\@tempa{#4}\def\@tempb{#5}\fi - \ifx\@tempa\UMO@minus \@tempdimb\@tempb\relax\fi - \multiply\@tempdima\UMO@specefactor \divide\@tempdima\@m - \multiply\@tempdimb\@m \divide\UMO@spacefactor - \@tempskipa#1 plus\@tempdima minus\@tempdimb\relax} -\def\@tempa#1 #2 #3 #4 #5\@nil{\def\UMO@plus{#2}\def\UMO@minus{#4}} -\@tempskipa1pt plus 2pt minus 3pt -\expandafter\@tempa\the\@tempskipa\@nil - -%%^L - -%% Processing Characters - -\def\UMO@charloop{\UMO@ifbgroup\UMO@charblock\UMO@icharloop} -\def\UMO@charblock#1{\UMO@putword - \UMO@putbox\relax{#1}\UMO@mode\UMO@afterblock \UMO@spacefactor\@m - \UMO@charloop} -\def\UMO@icharloop#1{\def\@tempa{#1}% - \ifx\@tempa\@nnil \UMO@putword \let\next\relax - \else\ifx\UMOspace#1\relax \UMO@putword \let\next\UMO@space - \else\ifx\UMOnewline#1\relax \UMO@putword \let\next\UMO@newline - \else - \ifnum`#1<256\relax \edef\UMO@theword{\UMO@theword#1}% - \ifnum\UMO@firstxspcode<\z@ - \UMO@firstxspcode\xspcode`#1\relax - \UMO@prebreakpenalty\prebreakpenalty`#1\relax - \fi - \UMO@lastxspcode\xspcode`#1\relax - \UMO@postbreakpenalty\postbreakpenalty`#1\relax - \else \UMO@putword \UMO@putchar{#1}\UMO@spacefactor\@m\fi - \let\next\UMO@charloop \fi\fi\fi \next} -\def\UMOspace{\PackageError{jumoline}% - {\string\UMOspace\space cannot be used here.}% - {\string\UMOspace\space can be used only in the argument of - \string\Underline\space and its relatives.}} -\def\UMOnewline{\PackageError{jumoline}% - {\string\UMOnewline\space cannot be used here.}% - {\string\UMOnewline\space can be used only in the argument of - \string\Underline\space and its relatives.}} - -%%^L - -%% Put ASCII String - -\def\UMO@putword{\ifx\UMO@theword\empty\else - \ifnum\UMO@mode=\UMO@afterchar - \advance\UMO@kpostbreakpenalty\UMO@prebreakpenalty - \penalty\UMO@kpostbreakpenalty - \ifdim\UMO@postjfmglue>\z@ \UMO@skip\UMO@postjfmglue - \else\ifodd\UMO@inhibitxspcode \ifodd\UMO@firstxspcode - \UMO@skip\xkanjiskip \fi\fi\fi\fi - \setbox\@tempboxa\hbox{% - \UMO@theword\global\UMO@spacefactor\spacefactor}% - \UMO@putbox\relax\UMO@theword \UMO@mode\UMO@afterword - \def\UMO@theword{}\fi \UMO@firstxspcode\m@ne} - -%%^L - -%% Put Kanji Letter - -\def\UMO@putchar#1{% - \ifnum\UMO@mode=\UMO@afterchar \UMO@prejfmglue\UMO@postjfmglue - \else \UMO@prejfmglue\z@ \fi - \UMO@postjfmglue\z@ - \ifnum`#1<\kuten"1001\relax\UMO@setjfmglue{#1}\fi - \@tempskipa\UMO@prejfmglue - \UMO@inhibitxspcode\inhibitxspcode`#1\relax - \@tempcnta\prebreakpenalty`#1\relax - \ifnum\UMO@mode=\UMO@afterchar - \advance\@tempcnta\UMO@kpostbreakpenalty - \ifdim\UMO@prejfmglue=\z@ \@tempskipa\kanjiskip \fi - \else\ifnum\UMO@mode=\UMO@afterword - \advance\@tempcnta\UMO@postbreakpenalty - \ifdim\UMO@prejfmglue=\z@ - \ifnum\UMO@lastxspcode>\@ne \ifnum\UMO@inhibitxspcode>\@ne - \@tempskipa\xkanjiskip \fi\fi\fi\fi\fi - \penalty\@tempcnta - \edef\@tempa{\the\@tempskipa}\ifx\@tempa\UMO@zskip\else - \UMO@skip\@tempskipa \fi - \UMO@putbox\inhibitglue{#1}% - \UMO@kpostbreakpenalty\postbreakpenalty`#1\relax - \UMO@mode\UMO@afterchar} -\@tempskipa\z@ -\edef\UMO@zskip{\the\@tempskipa} -\def\UMO@setjfmglue#1{% - \settowidth\@tempdima{$B$"(B#1}\settowidth\@tempdimb{$B$"(B\inhibitglue#1}% - \advance\@tempdima-\@tempdimb - \settowidth\UMO@tempdim{#1$B$"(B}\settowidth\@tempdimb{#1\inhibitglue $B$"(B}% - \advance\UMO@tempdim-\@tempdimb - \ifdim\@tempdima>\z@ - \ifdim\UMO@tempdim>\z@ - \@tempskipa\@tempdima minus\@tempdima\relax - \UMO@postjfmglue\UMO@tempdim minus\UMO@tempdim\relax - \else \@tempskipa\@tempdima minus\UMO@dqspace\relax \fi - \advance\UMO@prejfmglue\@tempskipa - \else \UMO@postjfmglue\UMO@tempdim minus\UMO@dqspace \fi} - -%%^L - -%% Draw Under/Mid/Overline - -\def\UMO@putbox#1#2{\setbox\@tempboxa\hbox{#1#2#1}\@tempdima\wd\@tempboxa - \ifUMO@firstelem\else - \rlap{\vrule\@height\UMO@height\@depth\UMO@depth\@width\@tempdima}\fi - \box\@tempboxa - \ifUMO@firstelem \UMO@firstelemfalse - \llap{\vrule\@height\UMO@height\@depth\UMO@depth\@width\@tempdima}\fi} -\def\UMO@skip#1{% - \leaders\hrule\@height\UMO@height\@depth\UMO@depth\hskip#1\relax} - -%%^L - -%% Explicit Spacing and Line Breaking - -\def\UMO@space{\UMO@mode\UMO@afterblock - \@ifstar\UMO@sspace\UMO@ispace} -\def\UMO@sspace#1{\vrule width\z@\nobreak\UMO@skip{#1}\UMO@charloop} -\def\UMO@ispace#1{\@tempskipa#1\relax - \@ifstar{\@tempswafalse\UMO@iispace}{\@tempswatrue\UMO@iispace}} -\def\UMO@iispace{\@ifnextchar[%] - {\UMO@penalty}% - {\UMO@skip\@tempskipa \UMO@charloop}} -\def\UMO@penalty[#1]{\@tempcnta#1\relax - \if@tempswa - \ifnum\@tempcnta<\z@ \@tempcnta-\@tempcnta \fi - \ifcase\@tempcnta \or - \@tempcnta\@lowpenalty \or - \@tempcnta\@medpenalty \or - \@tempcnta\@highpenalty \else - \@tempcnta\@M \fi - \ifnum#1<\z@ \@tempcnta-\@tempcnta \fi \fi - \penalty\@tempcnta \UMO@skip\@tempskipa \UMO@charloop} - -\def\UMO@newline{\UMO@mode\UMO@afterblock - \@ifstar{\UMO@skip{0pt plus1fil}\break \UMO@charloop}% - {\hfil \break \UMO@charloop}} - -%%^L - -%% Finalization - -\def\UMO@end{\ifnum\UMO@mode=\UMO@afterchar - \ifnum\UMO@kpostbreakpenalty>\z@ - \penalty\UMO@kpostbreakpenalty \fi - \ifdim\UMO@postjfmglue>\z@ - \UMO@skip\UMO@postjfmglue\fi \fi - \xdef\UMO@aftergroup{\ifnum\UMO@mode=\UMO@afterword - \spacefactor\number\UMO@spacefactor\fi}} -\endinput -%% -%% End of file `jumoline.sty'. diff --git a/en/sty/ruby.sty b/en/sty/ruby.sty deleted file mode 100644 index dc86aa86..00000000 --- a/en/sty/ruby.sty +++ /dev/null @@ -1,71 +0,0 @@ -% ruby.sty $B%k%S$r;H$&$?$a$N%9%?%$%k%U%!%$%k(B -% by y-takata@ics.osaka-u.ac.jp (~/.TAKABE) -% Mar. ix, mcmxcii -% May viii, mcmxcii modified -% Feb. viii, mcmxciii modified -% change glues on the end of ruby -% -% \ruby[]{<$B=O8l(B>}{<$B$U$j$,$J(B>} -% : <$B=O8l(B>$B$N>e$K(B<$B$U$j$,$J(B>$B$rIU$1$k%^%/%m!%(B -% <$B=O8l(B>$B$N:G>eJU$+$i(B\rubysep $B$@$1>e$K(B<$B$U$j$,$J(B>$B$N%Y!<%9%i%$%s$r(B -% $B9g$o$9!%(B -% -% []$BItJ,$r>JN,$7$?>l9g!$(B<$B=O8l(B>$B$r<+A3$JJ8;z4V3V$GAHHG$7!$(B -% <$B$U$j$,$J(B>$B$O!$(B -% (1)<$B=O8l(B>$B$NI}$h$jC;$1$l$P(B<$B=O8l(B>$B$NI}$K9g$o$;$F6QEy3d$jIU$1$9$k!%(B -% (2)<$B=O8l(B>$B$NI}$h$jD9$1$l$P(B<$B=O8l(B>$B$N:81&$K$O$_=P$5$;$k!%$D$^$j:8(B -% $B1&$N8l$N>e$K$+$V$5$;$k!%(B -% OPTIONS -% n : (narrow) -% <$B$U$j$,$J(B>$B$^$?$O(B<$B=O8l(B>$B$N6QEy3d$jIU$1$r$d$a$k!%(B -% s : (no sticking out) -% <$B$U$j$,$J(B>$B$,(B<$B=O8l(B>$B$N30$K$O$_=P$5$J$$$h$&$K$9$k!%$9$J$o$A!$(B -% <$B$U$j$,$J(B>$B$NI}$,(B<$B=O8l(B>$B$h$jD9$$>l9g!$(B<$B$U$j$,$J(B>$B$NI}$K9g$o$;$F(B -% <$B=O8l(B>$B$r6QEy3d$jIU$1$9$k!%(B -% -% \rubysep -% : <$B=O8l(B>$B$N:G>eJU$H(B<$B$U$j$,$J(B>$B$N%Y!<%9%i%$%s$N4V$N%0%k!$B$NBg$-$5$r7h$a$k(B -% $B%^%/%m!%(B -% -% $B;29M(B: -% $B1|B<@2I'(B: LaTeX $BH~J8=q:n@.F~Lg!$5;=QI>O@\wd\@rubyboxk \@rubyw\wd\@rubyboxr \fi\fi}% - \leavevmode\vbox{\kanjiskip\skip@% - \baselineskip\@rubyh -% \lineskiplimit=0pt \lineskip=0pt % $B=O8l$H%k%S$,=E$J$k$H$-$N%0%k!<(B - \hbox to\@rubyw{\hss\unhbox\@rubyboxr\hss} - \hbox to\@rubyw{\hss\unhbox\@rubyboxk\hss}}} - -% $B4A;z$H%k%S$N$9$-$^(B -\newlength{\rubysep} \rubysep=0pt - -% $B%k%S$N%U%)%s%H%5%$%:$rA*$V%k!<%k(B -\def\rubysize{% - \ifx\@currsize\large\scriptsize - \else\ifx\@currsize\Large\footnotesize - \else\ifx\@currsize\LARGE\small - \else\ifx\@currsize\huge\normalsize - \else\ifx\@currsize\Huge\normalsize - \else \tiny \fi\fi\fi\fi\fi} - -% $B$=$NB>(B -\chardef\@rubyboxk=0 -\chardef\@rubyboxr=2 -\def\@rubyw{\dimen@} -\def\@rubyh{\dimen@ii} - diff --git a/en/sty/trema.sty b/en/sty/trema.sty deleted file mode 100644 index a208d00c..00000000 --- a/en/sty/trema.sty +++ /dev/null @@ -1,61 +0,0 @@ -\newcommand{\tmpsection}[1]{} -\let\tmpsection=\section -\renewcommand{\section}[1]{% - \tmpsection{#1} - \vskip-2mm - {\color[gray]{.1}\hrule} - \vskip1zh -} - - -\newcommand{\tmpsubsection}[1]{} -\let\tmpsubsection=\subsection -\renewcommand{\subsection}[2]{% - \tmpsubsection{#2} - \vskip-6mm - \noindent{{\color[gray]{.6}\dotfill}} -} - - -\newcommand{\tmpsubsubsection}[1]{} -\let\tmpsubsubsection=\subsubsection -\renewcommand{\subsubsection}[2]{% - \tmpsubsubsection{{\color[gray]{.6}●}#2} -} - - -%% \renewenvironment{reviewcolumn}{% -%% \begin{screen} -%% }{% -%% \end{screen} -%% \vspace{2zw} -%% } - -\renewcommand{\reviewcolumnhead}[2]{% - {\noindent\large\headfont{#2}\vskip0mm{\color[gray]{.1}\hrule}}\vskip1zh -} - - -\renewcommand{\figurename}{{\color[gray]{.6}▲}Figure } -\renewcommand{\tablename}{{\color[gray]{.6}▲}Table } -\newcommand{\tmpcaption}[1]{} -\let\tmpcaption=\caption -\renewcommand{\caption}[1]{% - \headfont{\textbf{\tmpcaption{#1}}} -} - - -\makeatletter -\renewcommand{\reviewappendix}[0]{% - \if@openright\cleardoublepage\else\clearpage\fi - \thispagestyle{empty} - \refstepcounter{part} - \addcontentsline{toc}{part}{おわりに\hspace{1zw}} - \vspace*{2\Cvs} - {\parindent \z@ \raggedright - \normalfont - \interlinepenalty\@M - \Huge \headfont おわりに\par\nobreak - \vskip 3\Cvs} -} -\makeatother diff --git a/en/trema.css b/en/trema.css deleted file mode 100644 index 11dde657..00000000 --- a/en/trema.css +++ /dev/null @@ -1,114 +0,0 @@ -@charset "utf-8"; - -h1 { - color: #000080; - font-weight: bold; - text-align: center; -} - -h2 { - color: #000080; - font-weight: bold; - border-bottom: dotted 1px #000080; - margin-top: 1em; -} - -h3 { - color: #000080; - border-bottom: dotted 1px #000080; - margin-top: 1em; -} - -p.lead { - padding: 1em; - background: #c0c0ff; -} - -p.footnote { - font-size: xx-small; - padding: 1em; - background: #d0d0d0; -} - -p.sourcecaption { - font-weight: bold; - text-align: center; -} -p.imagecaption { - font-weight: bold; - text-align: center; -} -p.listcaption { - font-weight: bold; - text-align: center; -} -p.emlistcaption { - font-weight: bold; - text-align: center; -} -p.tablecaption { - font-weight: bold; - text-align: center; -} -p.notecaption { - font-weight: bold; - text-align: center; -} - -img { - width: 70%; - text-align: center; -} - -table { - border: solid 1px #000000; - width: 90%; - margin-bottom: 1em; - border-collapse: collapse; -} - -th { - border-bottom: solid 1px #000000; - background: #800000; - color: #ffffff; -} - -td { - border: solid 1px #000000; -} - -span.kw { - font-weight: bold; -} - -div.column { - padding: 0.5em; - background: #ffd0d0; - border: dotted 2px #808080; -} - -div.image { - text-align: center; -} - -div h5 { - font-size: larger; - border-bottom: solid 1px #000000; -} - -p { - text-indent: 1em; -} - -p.caption { - font-weight: bold; - text-indent: 0em; -} - -p.noindent { - text-indent: 0em; -} - -p.flushright { - text-align: right; -} diff --git a/en/trema.yaml b/en/trema.yaml deleted file mode 100644 index 17a36e7f..00000000 --- a/en/trema.yaml +++ /dev/null @@ -1,17 +0,0 @@ -bookname: trema -booktitle: Programming OpenFlow - -aut: Yasuhito Takamiya, Kazuya Suzuki -ill: Yuko Takamiya, Yasuhito Takamiya -trl: Haesung Hwang - -coverfile: _cover.html -stylesheet: trema.css -texstyle: ruby,trema -texdocumentclass: ["jsbook", "a4j"] -toclevel: 1 -secnolevel: 2 -epubversion: 3 -htmlversion: 5 -mytoc: true -params: --stylesheet=trema.css --subdirmode --chapterlink diff --git a/en/whats_openflow.re b/en/whats_openflow.re deleted file mode 100644 index a75b3fc5..00000000 --- a/en/whats_openflow.re +++ /dev/null @@ -1,216 +0,0 @@ -= What's OpenFlow? - -//lead{ -OpenFlow - one of the hottest topic nowadays - how is it structured and what are the advantages? We'll be using familiar examples rather than the difficult network related jargons to explain what it is. -//} - -//indepimage[incredible_machine][][width=8cm] - -== Control as you wish, by software - -//quote{ -Laziness: The quality that makes you go to great effort to reduce overall energy expenditure. It makes you write labor-saving programs that other people will find useful, and document what you wrote so you don't have to answer so many questions about it. Hence, the first great virtue of a programmer. Also hence, this book. - Programming Perl by Larry Wall et al. O'Reilly -//} - -Laziness is one of the most important hacker characteristics of a skilled programmer. Mulling over how to get a bag of potato chips without ever leaving the beloved computer might seem like an indolence from the ordinary people but it really is a serious matter from the hacker's point of view. - -Being lazy by utilizing the software is one area that hackers most specializes in, to exert their creativeness. The following three stories are great examples that utilize the high technical capabilities simply because of their laziness. - - 1. There was once a UNIX command in the MIT's AI laboratory (popular for being the hackers' haven), to order pizza online from the computer@{xpizza}. Order a box of pizza by typing a command when you get hungry from all the hacking you've done. Now how lazy is that? - 2. In the renowned Computer Science Department of Carnegie Mellon University, there was a eccentric vending machine called 'Coke Machine'@{coke_machine}. It lets you check how cold the coke is, by using a single UNIX command. Not surprisingly, it's to save you the pain of going all the way to the vending machine and to get a lukewarm can of coke. - 3. The Hyper Text Coffee Pot Control Protocol is specified in RFC 2324@{rfc2324}. The interface for remotely monitoring the amount of coffee in the pot and automatically brewing the coffee is defined. It's an April Fools' Day RFC so the whole thing is supposed be a joke but it's surprising that someone had actually implemented it. - -//footnote[xpizza][MITの@{xpizza} command manual: @{http://stuff.mit.edu/afs/sipb/project/lnf/other/CONTRIB/ai-info}] -//footnote[coke_machine][Carnegie Mellon Computer Science Department Coke machine: @{http://www.cs.cmu.edu/~coke/}] -//footnote[rfc2324][RFC 2324: @{http://www.ietf.org/rfc/rfc2324.txt}] - -The largest-scaled example of these 'have/hack fun using software' is the state-of-art data center. I'm sure a lot of the readers of this book have heard that the data centers supporting the cloud services are operated by only few engineers and most management process is automated to the utmost limit by softwares. From the recreational activities involving pizza, coke, and coffee to the rather tricky data centers, controlling 'something' just as you wish by software is diverting than anything and actually a worthwhile hack. - -== SDN: Let's control the network with software! - -OpenFlow is one of the technologies that you can use to hack the network. To put it simply, OpenFlow is one of the standard protocols used for controlling the behavior of the network switch. Even the entire network can be defined by a source code, since using the OpenFlow lets you rewrite the behavior of each switch freely with the software. This is called SDN (Software Defined Networking) and OpenFlow is attracting an attention as a typical technology to realized the SDN. - -Network is now considered as something programmable by the advent of the OpenFlow. We can almost hear a voice saying 'I can't believe you're still managing the network manually! Just let the software take care of it!'. I'm sure there are lots of places where you can be lazy about. - -When the creativity as strong as the hackers and the programmable characteristics of the OpenFlow are combined, the following 'ultimate automation' wouldn't feel like a fantasy anymore. - - * A network with a centralized control system by utilizing all sorts of collected data such as faults and traffic information - * A network architecture that can automatically changed depending on the user or application addition/deletion - * A network that can optimize the utilization of conventional infrastructure without extra investment - -All three topics will be dealt in this book. We'll be looking at some specific adaptation examples of the OpenFlow - from the small and medium-sized network such as home and work to the very-large-scaled data center's network - and answer simple questions such as 'What's OpenFlow and where would it come in handy?'. In addition, for those of you programmers who's thinking 'I want to create a brilliant network with this OpenFlow right away!', we have a lot of practical codes that you can actually run. - -You don't need to have an in-depth knowledge on the computer network or programming when you read this book. We'll be explaining one-by-one from the basics and the book is for almost anyone - network professionals, programmers, system engineers, sales people and managers - to understand right away if you have a pinch of an interest in the OpenFlow. Let's begin by understanding the mechanism of the OpenFlow! - -== Mechanism of the OpenFlow - -To explain the mechanism of the OpenFlow, we'll be using a little allegory. Think of a customer support center. Yes, it's that place with a toll-free number where you call when your TV or computer starts acting weird. Okay, but how's that related to the OpenFlow? - -Actually the basic mechanism of the OpenFlow is very similar to that of the customer support center. If you can understand the following two stories, it would be virtually the same as understanding the OpenFlow about 95%. So let's welcome three people; the main actor Yutaro, telephone operator Ms. Aoi, and her boss Mr. Miyasaka! - -=== Story 1:Aww my AC isn't working - -It's that time of the year when you're glad that you have your AC. However, something seems wrong with Yutaro's AC so he decides to dial the customer support center's number listed on the instructions handbook. -He checks every item in doubt suggested by the automatic support voice but to no avail. In the end, he gives up and decides to talk to the operator. - -"Hello, customer support center's Aoi. How may I help you?" - -Ms. Aoi is a telephone operator working for Yoyodyne Air Conditioner. Her job is to transfer the call from the customer with an AC problem to the engineer. (@{yoyodyne_support})。 - -//image[yoyodyne_support][Telephone operator transfers the inquiries from the customer to the appropriate engineer][width=12cm] - -"Something is wrong with my AC remote control. The temperature screen keeps blinking - how do I make this stop?" - -Ms. Aoi reaches for the manual by her side and opens it (@
{manual1}). In the manual, the source of malfunction and the extension number of the engineer who can fix the problem is written. The following figures are the number of inquiries for each malfunction. - -//table[manual1][Manual book for telephone operators]{ -Source of malfunction Extension of engineer in charge Number of inquiries ------------------------------------------------------------------- -Remote control 555-2222 8 cases -Interior evaporating unit 555-4444 6 cases -Exterior condensing unit 555-3333 4 cases -//} - -The entry was conveniently listed right on the top of the manual. - -"I'm sorry for your inconvenience. I'll forward you to the engineer in charge right away." - -When the transfer is done, Ms. Aoi updates the number of inquiries on remote control from 8 to 9 (@
{manual2}). - -//table[manual2][Updating the number of cases]{ -Source of Malfunction Extension of engineer in charge Number of inqueries ------------------------------------------------------------------- -Remote control 555-2222 @{9 cases} -Interior evaporating unit 555-4444 6 cases -Exterior condensing unit 555-3333 4 cases -//} - -//noindent -This way, Ms. Aoi can report how many cases there were for each malfunction and have the development department make use of this feedback for the next season's product. - -==== The story in the OpenFlow world - -In the realm of the OpenFlow, the source host of the packet and the OpenFlow switch that forwards the packet are, the customer Yutaro and the telephone operator Ms. Aoi, respectively (@{openflow_host_switch}). When the host sends packets, the OpenFlow switch forwards the packets to the appropriate destination according to the content of the packets. This is just like Ms. Aoi forwarding the call to the engineer in charge according to the inquiries that Yutaro made. - -//image[openflow_host_switch][Host = customer, switch = telephone operator, and flow table = manual][width=12cm] - -In an OpenFlow switch, this transactions are in the 'manual'. In the example of the customer support, Ms. Aoi looked up the extension line information in the manual. In an OpenFlow switch, the decision where to forward the packets are made by looking up the database in the switch called 'flow table'. As everything that Ms. Aoi does are manualized, everything that the OpenFlow switch does is determined by the content of this flow table. - -==== Flow table, where forwarding information is managed - -In a flow table, rules such as 'When this kind of packet arrives, forward it to the port x' are stored. These rules are called flow entries. Flow entries correspond to the items in the manual book for instance, 'When there's an inquiry on the malfunctioning remote control, forward to extension 555-2222'. - -Let's take a look at an example of an actual flow table. In Table @
{story1_openflow}, each entry corresponds to a flow entry composed of three elements; matching rule, action, and statistics. - -//table[story1_openflow][Example of flow table and flow entry]{ -Matching rule Action Statistics -------------------------------------------------------------------------------- -Source IP address: 192.168.1.100 Forward to Port 8 80 packets -Destination IP address: 192.168.10.92 Forward to Port 10 14 packets -Source MAC address: 00:50:56:c0:00:08 Forward to Port 1 24 packets -//} - -: Matching rule -Matching rule is used as a 'condition' to find out how the arrived packet should be processed from the flow table. As the forwarding destination was chosen based on the inquiry on 'something is wrong with the remote control', the process method, i.e. the action, is determined based on the matching rule that fits the trait of the packet. - -: Action -Action corresponds to a 'process method', that is, how to handle the arrived packet. Just like 'Transfer to extension 555-4444', the action is something like 'Forward to port number 8 of the switch'. However, the action isn't limited to simple forwarding. It can be about rewriting or dropping the packet. - -: Statistics -Statistics is a recording of how many packets were processed for each flow entry. As '9 inquiries were related to the remote control' was noted in the manual, information such as '80 packets were forwarded based on this flow entry' is stored. - -What did you think of that? Isn't the customer support center example and the OpenFlow really similar? The mechanism of the OpenFlow is very simple and easily comprehensible. - -=== Story 2:The AC isn't working again! - -The AC was working okay for a while but a month later, it's acting weird again. Yutaro dials the customer center once again. - -"The drain hose gets clogged really easily" - -Ms. Aoi flips through the manual pages but there aren't any sections on the drain hose! Apparently, it seems to be a whole new kind of problem. - -"I'm sorry sir, could you hold on for a moment? I'll see if I can reach the engineer who can guide you." - -Then there's the moment of waiting with please-hold-on-message and a pleasant music in the background. - -//image[yoyodyne_support_miyasaka][When you can't find what you're looking for in the manual, ask the boss][width=12cm] - -At a time like this, it's Mr. Miyasaka who Ms. Aoi always leans to (@{yoyodyne_support_miyasaka}). - -"Mr. Miyasaka, there's an inquiry on the drain hose. To whom should I forward the call to?" - -"Oh, in that case, Mr. Yamamoto should be the perfect person to ask" - -Ms. Aoi returns to the call with the information. - -"Thanks for waiting, I'll forward your call to the engineer in charge" - -This took more time compared to the very first inquiry on the remote control but at last the case is closed. Furthermore Ms. Aoi adds the number of Mr. Yamamoto's extension number informed by Mr. Miyasaka in the manual(@
{manual3}). When there's an inquiry on the duct hose in the future, she can answer quickly on the matter. - -//table[manual3][Updates the manual by adding a new source of malfunction and the contact information]{ -Source of Malfunction Extension of engineer in charge Number of inquiries ----------------------------------------------------------------------- -Remote control 555-2222 9 cases -Interior evaporating unit 555-4444 6 cases -Exterior condensing unit 555-3333 4 cases -@{Duct hose} @{555-5555} @{1 case} -//} - -==== The story in the OpenFlow world - -In the OpenFlow, the boss is a software called 'controller' (@{openflow_host_switch_controller}). When programming the network with OpenFlow, it's the controller part that the programmers code. You can control the network freely by coding the controller inside your brain to a software. - -//image[openflow_host_switch_controller][When the packet entry is not in the flow table, ask the controller][width=12cm] - -The packets specified in the flow table are forwarded by the switch at speed but there are packets without such instructions, making the switch lost in the dark. In this case, the switch asks the controller what to do with the packet. The controller inspects the content of the packet and gives an instruction what to do, i.e. writes the flow entry in the flow table. - -When the packet that doesn't have any forwarding information in the flow table arrives, the forwarding speed becomes slow since the switch has to ask the controller. However, the process is fast just with the switch if the controller writes the necessary flow entries in advance when the switch starts up. - -=====[column] Question from Yutaro: How slow is querying the controller? - -What happens if the controller gives out instructions every time without using the flow table? The answer - a LOT slower, like several manyfold. To test, we wrote a short program in our testing environment and compared the forwarding by the software switch and processing everything by the controller. Guess what, 5 times slower!! Of course this is just a rough estimation but it should give you a rough idea about how slow it gets. In addition, if a hardware switch was used instead of the software switch in the comparison, the difference would have been larger. - -=====[/column] - -== The Joy of OpenFlow - -I'm sure you now understand the nuts and bolts of the OpenFlow. So let's get to the bottom line, how is OpenFlow going to make my life happy? - -=== Automation and system collaboration is easy - -In a customer support center, everything is taken care automatically by the telephone operators if there's an appropriate manual made in advance. A perfect division of labor - overall monitoring done by the manager and the actual work done by the telephone operators. This way the manager can focus on collaborating/coordinating with other departments while the telephone operator does the actual job. - -In a similar way, automating the network operation is easy since controlling the OpenFlow switch is implemented by the 100% software controller. In addition, if the controller is written by well known languages such as Ruby, Python, and Java, collaborating with conventional systems and services is convenient. For example, an advanced automation is possible if the network configuration can be modified by triggers such as problem alert, application request, and business policy update. - -=== Centralized control of network traffic is convenient - -The estimation of congestion level or controlling the traffic is an easy task since every information of the number of inquiries is reported to the managerial level in the customer support center. The manager can instruct the operators through the manual book to have the calls shared among the engineers, if a specific engineer is bombarded with inquiries. On the other hand, if each and every operator makes a separate decision, they might end up forwarding the calls to the same engineer. - -In the OpenFlow, the traffic can be optimized with all things considered since the traffic information is reported to the controller. The controller can collect all sorts of traffic data and its statistical information from the whole network. By updating the flow table of each switch based on the information, the optimal route of the packet can be estimated. Conversely, if each and every switch makes a separate decision, the traffic can't be distributed evenly. - -=== Techniques and tools for developing software can be used - -The controller is a kind of a software. Therefore, many years of software development's various techniques and tools can be applied to constructing the network. - - * If the controller is developed using the agile development which is the recent mainstream, the functions can be added repetitively. The network can be gradually constructed, getting version upgrades with the feedbacks. - * The whole network can be tested automatically by writing controller's unit test or acceptance test. The output of the test result becomes a part of the specifications and there's no need to manage a separate Excel or Word file. - * If the controller's source code or related data are managed by version control tools such as git, managing the entire network's version, checking the difference of two versions, and reverting may be possible. - -====[column] Mr. Torema says:OpenFlow is a conveyor belt sushi!? - -Conventional routers and switches demands you to use their vendor-defined functions. For example, even when you want to use only 10 functions, you have to buy the router that comes with 100 pre-defined features. This is like going to a French restaurant where you can only order full course cuisine! Even if you are using only a few features, there are millions of point of failures and it isn't always easy to pinpoint the cause or debug. -OpenFlow is like a conveyor belt sushi. It might be difficult to mimic a dish from a fancy French restaurant but if you choose only the necessary functions and implement those, you might end up with something just as you intended. - -====[/column] - -== Some things you should be careful about OpenFlow - -Of course, not everything about OpenFlow makes you happy. You should be careful about the scalability issue when there's an increase in the number of the switch, since the controller does all the controlling in the OpenFlow. In the worst case, the controller might just stop working if too many packets that are not on the flow entry arrive at the controller at the same time. - -Also, you have to be extra careful about where to use the OpenFlow and the remaining flow table space. For example, if you connect the OpenFlow switch to the Internet where various kinds of packets exist, the controller will soon crash with all the inquiries that fills up the flow table. However in a closed environment such as data centers, you can pretty much guess the traffic characteristics and types of the packets in advance. That way, the scalability can be achieved even when the number of switches increase, by constructing the network and the flow entry so that only the minimal packets can reach the controller. - -== Wrap-up - -In this section, we gave an illustration on the OpenFlow that can realize SDN. OpenFlow consists of switches that have flow tables and a software controller that does the centralized control of the flow tables. By making the network control a software, you can get all the benefits such as automation, collaboration with various systems, traffic control, and application of software technology. - -In the next section, we'll be covering some specific use cases of OpenFlow. diff --git a/foreword.markdown b/foreword.markdown new file mode 100644 index 00000000..a97f7d2b --- /dev/null +++ b/foreword.markdown @@ -0,0 +1,13 @@ +
+ +# 第2版に寄せて + +何か気の利いたことを書く. + +アップデート内容を入れる + + - OpenFlow 1.3 に対応したよーとか + - 章を増やしてイロイロ見直したよーとか + - Cの部分をRubyにしたよーとか + +
diff --git a/foreword_edition1.markdown b/foreword_edition1.markdown new file mode 100644 index 00000000..ea07b8c2 --- /dev/null +++ b/foreword_edition1.markdown @@ -0,0 +1,29 @@ +
+ +# はじめに + +夕食前に自宅で原稿を書いていると、5歳になる長男が保育所で拾ってきた石を見せに来たものです。見ると何の変哲もない灰色の石ころですが、本人にとっては大切なものだそうで、「ぜったいに捨てないでね」と念を押してきます。そのうちこうした“たからもの”の石ころが机の上に5個、6個、とだんだんたまっていきました。 + +思えば、この本もそんな石ころ集めから始まりました。ソフトウェア工学の大家、ジェラルド・ワインバーグの最近の著書に『ワインバーグの文章読本——自然石構築法』(翔泳社 刊)があります。これは、多作なワインバーグじきじきの書き方指南で、メモや引用といった文章の断片を“石”と呼び、この石を集めたり並べ替えたりすることで立派な石造りの建物やアーチを組み上げる、つまり一冊書き上げる方法を説明した名著です。「よし、まずは石を集めるか」。自然石構築法を知った私は、さっそくこれに取りかかりました。 + +本書の執筆にあたって集めた石は、すぐに膨大な数になりました。OpenFlowの仕様書や論文はもちろん、職場での雑談、ソースコードの切れはし、大学での講義、メーリングリストで見つけた投稿、雑誌やWebの連載記事[^1]、気晴らしの読書やテレビ、昔よく聴いた夕方のラジオ番組、iPhoneに残っていた写真、とにかく大小いろんな色の石を、そこらじゅうから集めまくっては並べかえる毎日でした。 + +[^1]: 本書のいくつかの章は、Software Design 2011年11月号〜2012年5月号に連載した『こんな夜中にOpenFlowでネットワークをプログラミング![Trema編]』を大幅に加筆修正したものです。 + +こうして集めた石の中には、けっして独力では収集できなかった貴重な石も多くあります。本書の原稿をGitHubで公開したところ[^2]、たくさんの方々から100件以上のレビューをいただきました。石をくれる息子のように、こちらからお願いしなくとも善意でコメントを送ってくれる方が大勢いたのです。こうしたいくつかの石は、本書の重要な部分を占めています。特に第16章のTremaを使ったデータセンター、第17章のGoogleでのOpenFlowユースケース、そしてAppendix AのOpenFlowスイッチ自作法は、普通では絶対に手に入らない金ピカの宝石をいろいろな人からいただいたおかげで、何とか形にすることができました。 + +[^2]: https://github.com/yasuhito/trema-book + +“石集め”協力者のみなさん(敬称略、順不同):
+壬生亮太、宮下一博、石井秀治、金海好彦、@stereocat、高田将司、富永光俊、沼野秀吾、富田和伸、前川峻志、園田健太郎、大山裕泰、藤原智弘、空閑洋平、佛崎雅弘、阿部博、小谷大祐、笹生健、山口啓介、森部正二朗、高橋大輔、山本宏、橋本匡史、小泉佑揮、廣田悠介、長谷川剛、千葉靖伸、須堯一志、下西英之、角征典、高橋征義、早水悠登、弓削吉正、村田友範、上原正史、高宮友太郎、高宮葵、高宮優子。特に、次の方々には特別感謝です:@SRCHACK、栄純明、坪井俊樹、小谷大祐、黄恵聖、山崎泰宏、取口敏憲。 + +石はただ集めるだけではなく、必要な形に整形したり隙間を埋めたり、さらには磨き上げたりする必要があります。実は、私はもともとネットワークの専門家ではないので、ネットワークに特有な考え方や用語の説明に苦労しました。そうした部分を補ってくれたのが、ネットワーク研究者でありこの本の共著者でもある鈴木一哉氏でした。また私なりにも、石1つひとつの正確さにはベストを尽くしました。たとえばとある章に、酔っぱらいが三軒茶屋(東京都)から武蔵小杉(神奈川県)まで歩いて帰るというエピソードがありますが、私は本当に歩けることを体を張って実証したのです。 + +執筆と並行してやったのが、本書で取り上げたOpenFlowプログラミングフレームワークTremaの開発です。Tremaはもともと、その場しのぎで書いたソフトウェアを出発として、大量のテストコード、リポジトリサーバのクラッシュ、svnからgitへの乗り換え、二度の忘年会、いきなりの人事異動、インドとの長距離電話会議、を経験して鍛えられてきたフレームワークです。ずいぶんと曲折を経たものですが、まさしく石のような意思で乗り切りました。 + +私は、いいフレームワークといい本ができあがったと思っています。私のいいか悪いかの判断基準は、「まだ誰もやっていないことは、いいことだ」という単純な考えに基いています。Tremaみたいなフレームワークは、まだ誰もやってない。OpenFlowを正面きってここまで扱っている本は、まだ他にはない。だからどちらも私にとっては“いいもの”なのです。もちろん、本当にいいか悪いかは、読者のみなさんのご判断におまかせすることにします。 + +2012年12月1日
+高宮安仁(@yasuhito) + +
diff --git a/hello_trema.adoc b/hello_trema.adoc new file mode 100644 index 00000000..741eb752 --- /dev/null +++ b/hello_trema.adoc @@ -0,0 +1,555 @@ += Hello, Trema! +:imagesdir: images/openflow_framework_trema + +// TODO asciidocで「トレマ」をルビ表示にする +Trema(トレマ)を使うと楽しくSDNの世界が味わえます。これでいよいよあなたもOpenFlowプログラマの仲間入りです! + +== 作ってわかるOpenFlow + +いよいよOpenFlowを使ってネットワークを実際にプログラムしていきます。職場や自宅のような小規模ネットワークでもすぐに試せるコードを通じてOpenFlowの世界を体験しましょう。実際に手を動かし実行してみれば「OpenFlowってどんな場面で使えるの?」というよくある疑問も徐々に氷解していくでしょう。 + +実装はステップバイステップで進みます。どのステップも実用的な例となっており、最初はOpenFlowやプログラミングの基礎から始めます。そしてパッチパネルやL2スイッチ、ファイアウォール、ルータの実装など徐々に複雑な機能へとステップアップし、最終的にはデータセンターでも動く本格的な「ネットワーク仮想化」の実装を目標とします。 + + - Hello Trema (本章): OpenFlow 版 Hello World + - cbenchベンチマーク
 (3章): OpenFlow のマイクロベンチマークツール + - スイッチ監視ツール (4章): スイッチのスペックや転送量のモニタリングツール + - パッチパネル (5章):
ソフトウェアとして実装したインテリジェント・パッチパネル + - ラーニングスイッチ
 (6章): レイヤ2スイッチをエミュレートするコントローラ + - テスト駆動開発 (7章): リピータハブのテスト駆動開発 + - ブリッジ (8章): レガシーなネットワークとOpenFlowネットワークのブリッジ + - ファイアウォール (9章): 透過型ファイアウォール + - ルータ (10章,11章): 基本的なレイヤ3スイッチ (ルータ) + - トポロジ (12章): 中規模〜大規模ネットワークのトポロジ検知 + - ルーティングスイッチ (13章): 中規模〜大規模ネットワーク用の仮想レイヤ2スイッチ + - スライサブルスイッチ (14章): ルーティングスイッチに仮想ネットワーク機能を追加 + +まずは、OpenFlowプログラミングのためのフレームワーク、Tremaを改めて紹介します。 + +== Tremaとは + +TremaはOpenFlowコントローラを開発するためのフリーソフトウェアです。GitHub上でオープンに開発しており、ライセンスはGPL2のフリーソフトウェアです。その強力な機能や使いやすさから、国内外の企業や大学および研究機関などで採用されています。 + +Tremaの情報はおもに次のURLから入手できます。 + +- Tremaホームページ: https://trema.github.com/trema/ +- GitHubのプロジェクトページ: https://github.com/trema/ +- メーリングリスト: http://groups.google.com/group/trema-dev/ +- Twitterアカウント: https://twitter.com/trema_news + +Tremaの特徴は「プログラミングフレームワーク」をうたっていることです。いったいこれは何でしょうか? + +Webプログラミングを多少かじったことがあれば、プログラミングフレームワークと聞くとRuby on Rails footnote:[http://rubyonrails.org/]を真っ先に思い浮かべるでしょう。Railsの登場以前、90年代半ばには原始的なCGIプログラミングがWeb開発の主流でした。HTTPプロトコルを意識した低レベルなコードをCやPerlで書かねばならず、ごく単純な掲示板サービスを作るのにも大量のコーディングが伴いました。しかし2000年代に入り、より生産性の高い開発手法 — プログラミングフレームワークによるアジャイル開発 — によって一気にWebサービスは「カンブリア爆発」を迎えました。Railsを代表とするWebプログラミングフレームワークは、HTTPプロトコルの詳細を抽象化した高レベルなAPIを提供します。また、RubyやPythonをはじめとするスクリプティング言語の採用や、開発全体をラップトップPC1台で完結できる数々の開発支援ツールの提供によって、生産性を劇的に向上しました。 + +この流れをOpenFlow界にも吹き込んだのがTremaです。Tremaは「OpenFlow版Rails」を合言葉として、2011年に初のOpenFlowプログラミングフレームワークとして登場しました。開発言語にはRailsと同じくRubyを採用し、また高レベルなOpenFlow APIを提供することで、プログラマはごく短いコードでOpenFlowコントローラを実装できます。また強力なOpenFlow開発ツール群を提供することで、ソフトウェアテストを中心とした反復的で段階的なアジャイル開発を可能にします。 + +こうした強力なツールの一つがTremaの仮想ネットワーク機能です。OpenFlowスイッチを持っていない開発者でも、Tremaを使えばラップトップPC一台の中に仮想的なOpenFlowネットワークを作り、そこで自分の開発したコントローラを実行できます。この「作ったものをすぐに実行できる」という利点は、生産性の向上だけでなくSDNやOpenFlowのような新しい技術の習得にもつながります。正しい理解のためには概念の理解に加えて実践、つまり実際に手を動かすことが欠かせないからです。 + +ここからは実際にTremaを使ってOpenFlowコントローラを作り、そして動かしていきます。まずはTremaの実行環境をセットアップしましょう。 + +[NOTE] +.Tremaの由来は? +==== +Tremaの名前は、著者の一人がファンである「とれまレコード (http://www.fumiyatanaka.com/toremarecords/) 」という大阪の小さなレコードレーベルの名前から来ています。とれまレコードのリリースする楽曲は国内よりもむしろ海外で人気があり、海外のクラブチャートにもよくランクインします。 + +この「とれまレコード」の名前にも面白い由来があります。日本がバブルの頃、道路の「とまれ」という標示がよく「とれま」と間違えて描かれており、これに目をつけたレーベルオーナーが「とれまレコード」と名付けたのだそうです。そしてもともと、このありえないミスの原因は、バブル景気時代に急増した外国人労働者達が、日本語もままならないまま工事現場で働いていたということにあります。 + +[[trema_logo]] +image::trema_logo.png[caption="図2-a",title="Tremaの公式ロゴ"] + +この逸話にのっとって、Tremaの公式ロゴも<>のとおり道路標識の写真になっています。ちなみに、こんな道路標識は日本中どこを探してもありません! 本書の編集者が画像編集ソフトで試しに作ってみたところ評判が良かったので、そのまま公式ロゴになりました。 +==== + +== Trema実行環境のセットアップ + +TremaはLinux用のソフトウェアです。次のLinuxディストリビューションでの動作を確認しています。 + +- Ubuntu (i386/amd64) +- Debian GNU/Linux (i386/amd64) + +なお動作保証はしていませんが、CentOSやFedoraをはじめとするRedHat系Linuxディストリビューションでの動作も確認しています。 + +// TODO それぞれの動作バージョンを明記したい。phutで呼び出しているipコマンドとか、あまり古いカーネルを使っていると動かないはず。 + +`trema` コマンドの実行には sudo 権限が必要です。あらかじめ、`sudo` を使って root 権限でコマンドを実行できるかどうか、`sudo` の設定ファイルを確認しておいてください。 + +`sudo` が正しく設定できることを確認したら、Tremaの実行に必要な次のソフトウェアをインストールします。 + +- Rubyインタプリタ : Tremaの実行にはRubyのバージョン2.0以降が必要です。Tremaを使ったコントローラの開発にもRubyを使います。 +- Bundler (https://bundler.io/) : Rubyライブラリのインストーラです。bundlerを使ってTrema本体と実行に必要なライブラリをインストールします。 +- Open vSwitch (http://openvswitch.org/) :
OpenFlowに対応したソフトウェアスイッチの一種です。Tremaの仮想ネットワーク機能で使用します。 + +// TODO ここではTremaはインストールしない、ということを書くべき? + +=== RubyとBundlerのインストール + +Rubyのインストールには、RVMfootnote:[https://rvm.io/]というRubyインストーラを使うのが手軽です。次のコマンドを実行すると、安定版のRubyを自動的にインストールしてくれます。 + +---- +$ curl -sSL https://get.rvm.io | bash -s stable --ruby +---- + +Bundlerは次のコマンドでインストールできます。 + +---- +$ gem install bundler +---- + +なお `gem` はRubyの標準ライブラリ形式 `.gem` をインストールするコマンドです。ここでは最新版のBundlerの `.gem` を自動的にダウンロードしてインストールしています。 + +=== Open vSwitchのインストール + +Open vSwitchは `apt-get` で簡単にインストールできます。 + +---- +$ sudo apt-get install openvswitch-switch +---- + +他のディストリビューションを使う場合は、コマンド名やパッケージ名を適宜読み替えてください。 + +// TODO CentOSやFedoraでのインストール方法を誰かに聞いて書く + +以上でTremaを使うための準備が整いました。それでは早速、入門の定番Hello, Worldを書いて実行してみましょう。 + +== Hello, Trema! + +「Hello Trema!」は最も簡単なOpenFlowコントローラです。その唯一の機能は、スイッチと接続し「Hello, 0xabc!(0xabcはスイッチのDatapath ID)」と表示するだけです。このように機能は単純ですが、そのソースコードはTremaでコントローラを作るのに必要な基本知識をすべて含んでいます。 + +=== Hello Tremaを書く + +コントローラの実装はプロジェクト用ディレクトリを作ることから始めます。まずは次のように、「Hello Trema!」用の空のディレクトリhello_tremaと、ソースコード用ディレクトリhello_trema/libを `mkdir -p` コマンドで新たに作ってください。 + +---- +$ mkdir -p hello_trema/lib +$ cd hello_trema +---- + +==== プロジェクトディレクトリの中身 + +プロジェクトディレクトリには、コントローラに関連するすべてのファイルを置きます。コントローラのソースコードをはじめ、README.mdやLICENSEといったドキュメント類、コントローラの動作をテストするためのテストファイル、そして各種設定ファイルがここに入ります。 + +プロジェクトディレクトリのお手本として、GitHubのtrema/hello_tremaリポジトリ(https://github.com/trema/hello_trema) を見てみましょう。このリポジトリは、標準的なRubyプロジェクトのファイル構成に従っています。次に主要なファイルを挙げます。 + +- README.md: メインのドキュメント +- LICENSE: 配布ライセンスの指定 +- CHANGELOG.md: 開発履歴 +- Gemfile: 必要なgemパッケージの定義 +- Rakefile: 開発用タスク +- lib/: コントローラの実装 +- features/: 受け入れテスト +- spec/: ユニットテスト +- tasks/: 開発用タスク定義 + +自分で作ったコントローラを公開する場合、このようなファイル構成にすることが求められます。 + +[NOTE] +==== +テスト関連のディレクトリ(features/, spec/, tasks/)の用途については、第7章「テスト駆動開発」で詳しく説明します。 +// TODO 第7章にリンクを張る +==== + +==== コントローラ本体の実装 + +エディタでhello_tremaディレクトリ内のlib/hello_trema.rbを開き次のRubyコードを入力してください。.rbはRubyプログラムの標準的な拡張子です。Rubyの文法は必要に応じておいおい説明しますので、もしわからなくても気にせずそのまま入力してください。 + +[source,ruby,subs="verbatim,attributes"] +.lib/hello_world.rb +---- +# Hello World! +class HelloTrema < Trema::Controller + def start(_args) + logger.info 'Trema started.' + end + + def switch_ready(datapath_id) + logger.info "Hello #{datapath_id.to_hex}!" + end +end +---- + +// TODO vendor/hello_trema/lib/hello_trema.rbからソースコードを直接importする + +==== スイッチの定義 + +Hello Tremaの実行にはOpenFlowスイッチが1台必要です。さきほどインストールしたOpen vSwitchをHello Tremaコントローラに接続することにしましょう。次の設定ファイルtrema.confをエディタで作成してください。 + +[source,ruby,subs="verbatim,attributes"] +.trema.conf +---- +vswitch { datapath_id 0xabc } +---- + +この設定ファイルでは0xabcというDatapath IDを持つ1台のソフトウェアスイッチを定義しています。コントローラを実行する際にこの設定ファイルを指定することで、Open vSwitchを起動しコントローラに接続できます。 + +==== Tremaのインストール + +Hello Tremaの実行にはもちろんTremaが必要です。実行に必要なRubyのアプリケーションやライブラリを`Gemfile`というファイルに次のように書いておくと、Hello Trema専用の実行環境を自動的にセットアップできます。 + +[source,ruby,subs="verbatim,attributes"] +.Gemfile +---- +source 'https://rubygems.org/' # <1> + +gem ‘trema’# <2> +---- +<1> gemの取得元として標準的なhttps://rubygems.orgを指定します +<2> 実行環境にTremaを追加します + +次のコマンドを実行すると、Tremaの実行コマンドがbin/tremaにインストールされます。 + +---- +$ bundle install --binstubs +$ ./bin/trema --version +trema version 0.5.1 +---- + +実行に最低限必要なコードはこれだけです。それでは細かい部分は後で説明するとして「習うより慣れろ」でさっそく実行してみましょう。 + +==== 実行してみよう(trema run) + +作成したコントローラは `trema run` コマンドですぐに実行できます。Rubyはインタプリタ言語なので、コンパイルの必要はありません。ターミナルで次のように入力すると、この世界一短いOpenFlowコントローラはフォアグラウンドプロセスとして起動し、画面に「Trema started」「Hello, 0xabc!」と出力します。 + +---- +$ ./bin/trema run ./lib/hello_trema.rb -c trema.conf +Trema started. +Hello, 0xabc! # <1> +$ +---- +<1> `Ctrl + c` でコントローラを終了 + +ここまで見てきたように、`trema` コマンドを使うと、とても簡単にコントローラを実行できます。`trema` コマンドには他にもいくつかの機能がありますので、ここで簡単に紹介しておきましょう。 + +== trema コマンド + +`trema` コマンドは Trema 唯一のコマンドラインツールであり、コントローラの起動やテストなどさまざまな用途に使います。 + +たとえば先ほどの「Hello, Trema!」で見たように、`trema run` はコントローラを起動するためのコマンドです。起動したコントローラは OpenFlow スイッチと接続しメッセージをやりとりします。また、`trema run` コマンドは `-c` (`--conf`) オプションで仮想ネットワークを作ることもでき、作ったコントローラをこの仮想ネットワークの中でテストできます(<>)。 + +[[trema_run_command]] +image::trema_overview.png[caption="図2-1",title="trema runコマンドの実行イメージ"] +// TODO 図から実ネットワークを消して、-cオプションと仮想ネットワークの対応を付ける + +`trema` コマンドは `git` や `svn` コマンドと似たコマンド体系を持っており、`trema` に続けて `run` などのサブコマンドを指定することでさまざまな機能を呼び出します。`trema` コマンドは Trema フレームワークにおける中心的なツールで、あらゆるコントローラ開発の出発点と言えます。こうしたコマンド体系を一般に「コマンドスイート」と呼びます。 + +// TODO コマンドスイートの一般的なオプション体系をコラムで詳しく説明 + +一般的なコマンドスイートと同じく、サブコマンドの一覧は `trema help` で表示できます。また、サブコマンド自体のヘルプは `trema help [サブコマンド]` で表示できます。以下に、`trema help` で表示されるサブコマンド一覧をざっと紹介しておきます。いくつかのサブコマンドはまだ使い方を紹介していませんが、続く章で説明しますので今は目を通すだけでかまいません。 + +* `trema run` +
コントローラをフォアグラウンドで実行する。`--daemonize` (`-d`) オプションを付けることで、コントローラをバックグラウンド (デーモンモード) として実行できる +* `trema version` +
Trema のバージョンを表示する。`trema --version` と同じ +* `trema killall` +
バックグラウンドで起動している Trema プロセス全体を停止する +* `trema kill` +
仮想ネットワーク内の指定したスイッチまたはスイッチポートを停止する +* `trema up` +
仮想ネットワークの指定したスイッチまたはスイッチポートを再び有効にする章) +* `trema send_packets` +
仮想ネットワーク内でテストパケットを送信する +* `trema show_stats` +
仮想ネットワーク内の仮想ホストで送受信したパケットの統計情報を表示する○章) +* `trema reset_stats` +
仮想ネットワーク内の仮想ホストで送受信したパケットの統計情報をリセットする(第○章) +* `trema dump_flows` +
仮想ネットワーク内の仮想スイッチのフローテーブルを表示する + +// TODO relishで生成したドキュメントと対応付けて更新 + +では、気になっていた Ruby の文法にそろそろ進みましょう。今後はたくさん Ruby を使いますが、その都度必要な文法を説明しますので心配はいりません。しっかりついてきてください。 + +== 即席Ruby入門 + +Rubyを習得する一番の近道は、コードを構成する各要素の種類(品詞)を押さえることです。これは、外国語を習得するコツに近いものがあります。ただし外国語と違い、Rubyの構成要素にはその品詞を見分けるための視覚的なヒントがかならずあります。このためRubyのコードはずいぶんと読みやすくなっています。 + +|======================================================================== +| 品詞 | 視覚的ヒント | 例 + +| 定数 | 大文字で始まる | HelloTrema, Trema::Controller +| インスタンス変数 | @で始まる | @switches, @name +| シンボル | :で始まる | :match, :actions +|======================================================================== + +[NOTE] +==== +インスタンス変数とシンボルの使いかたについては、後の章で詳しく説明します。 +==== + +このように最初の文字を見れば、それがどんな品詞かすぐにわかります。たとえば、大文字で始まる名前はかならず定数です。品詞がわかれば、そのRubyコードがどんな構造かも見えてきます。これからそれぞれの品詞について簡単に説明しますが、最初からすべてが理解できなくとも構いません。しばらくすれば「Hello, Trema!」のあらゆる部分が識別できるようになっているはずです。 + +=== 定数 + +`HelloTrema` や `Trema::Controller` など、大文字で始まる名前が定数です。Rubyの定数は英語や日本語といった自然言語における固有名詞にあたります。 + +[source,ruby,subs="verbatim,attributes"] +.lib/hello_world.rb +---- +# Hello World! +class HelloTrema < Trema::Controller # <1> + def start(_args) + logger.info 'Trema started.' + end + + def switch_ready(datapath_id) + logger.info "Hello #{datapath_id.to_hex}!" + end +end +---- +<1> この行の `HelloTrema` と `Trema::Controller` が定数 + +英語でも固有名詞は大文字で始めることになっています。たとえばTokyo Tower(東京タワー)もそうです。東京タワーは動かすことができませんし、何か別なものに勝手に変えることもできません。このように、固有名詞は時間とともに変化しないものを指します。そして固有名詞と同様、Rubyの定数は一度セットすると変更できません。もし変更しようとすると、次のように警告が出ます。 + +---- +$ irb +> TokyoTower = "東京都港区芝公園4丁目2-8" +> TokyoTower = "増上寺の近く" +(irb):2: warning: already initialized constant TokyoTower +(irb):1: warning: previous definition of TokyoTower was here +=> "東京都港区芝公園4丁目2-8" +---- + +// TODO できれば、rake のビルド時にこれを実行して出力をここに入れる + +`class` に続く定数はクラス定義です。「Hello, Trema!」の例では `HelloTrema` が定義されるクラス名です。「`class` +クラス名」から始まるクラス定義は、同じ字下げレベルの `end` までの範囲です。 + +[source,ruby,subs="verbatim,attributes"] +.lib/hello_trema.rb +---- +class HelloTrema < Trema::Controller # <1> + def start(args) + logger.info "Trema started." + end + + def switch_ready(datapath_id) + logger.info "Hello #{datapath_id.to_hex}!" + end +end # <2> +---- +<1> HelloTremaクラス定義の始まり +<2> クラス定義の終わり + +==== コントローラクラスの継承 + +Tremaではすべてのコントローラはクラスとして定義し、Tremaの提供する `Trema::Controller` クラスをかならず継承します。クラスを継承するには、`class クラス名 < 親クラス名` と書きます. + +[source,ruby,subs="verbatim,attributes"] +---- +class HelloTrema < Trema::Controller # <1> + ... +end +---- +<1> `Trema::Controller` クラスを継承した `HelloTrema` クラスを定義 + +`Trema::Controller` クラスを継承することで、コントローラに必要な基本機能が `HelloTrema` クラスにこっそりと追加されます。たとえば雑多な初期化などの裏仕事を `Trema::Controller` クラスが代わりにやってくれるわけです。 + +=== ハンドラの定義 + +さて、こうして定義した `HelloTrema` クラスはどこから実行が始まるのでしょうか? Cで言う `main()` 関数に当たるものがどこにも見あたりません。 + +その答はTremaの動作モデルであるイベントドリブンモデルにあります。Tremaのコントローラは、さまざまなOpenFlowイベントに反応するイベントハンドラメソッド(以下、ハンドラと呼びます)をまとめたクラスとして定義します。 + +ハンドラの定義は `def` に続く名前から `end` までのブロックです。たとえば `HelloTrema` の例では `start` と `switch_ready` ハンドラを定義しています。 + +[source,ruby,subs="verbatim,attributes"] +---- +class HelloTrema < Trema::Controller + def start(args) # <1> + logger.info "Trema started." + end + + def switch_ready(datapath_id) # <2> + logger.info "Hello #{datapath_id.to_hex}!" + end +end +---- +<1> `start` ハンドラの定義 +<2> `switch_ready` ハンドラの定義 + +// TODO こういうAPIの用語集をasciidoctorで作っておく +// TODO: なぜargsをアンダースコアで始めるのか、rubocopを交えながら説明する。 +// TODO: 可能であればhello_tremaのソースコードから部分的にimportする + +それぞれのイベントハンドラは、対応するイベントが発生したときに自動的に呼び出されます。たとえば `start` ハンドラはコントローラの起動イベント発生時、つまり `trema run` でコントローラを起動したときに自動的に呼ばれます。また、Packet Inメッセージ(第3章)が到着したとき、もし `packet_in` ハンドラがコントローラクラスに定義されていれば、Tremaが `packet_in` ハンドラを自動的に呼びます。 + +// TODO これを説明する図をここに入れる + +Tremaでよく使うイベントをリストアップしておきます。 + +- `start` +
コントローラの起動時に呼ばれる +- `switch_ready` +
スイッチがコントローラに接続したときに呼ばれる +- `switch_disconnected` +
スイッチがコントローラから切断したときに呼ばれる(第4章) +- `packet_in` +
未知のパケットが到着したというPacket Inメッセージ到着時に呼ばれる(第3章) +- `flow_removed` +
フローが消されたときのFlow Removedメッセージ到着時に呼ばれる(第6章) + +[NOTE] +.ハンドラの自動呼び出し +==== +「ハンドラメソッドを定義しただけで、なぜ自動的に呼び出せるんだろう?」と不思議に思う人もいるでしょう。コード中にどんなメソッドがあるか? というコンパイル時情報をプログラム自身が実行時に知るためには、言語処理系の助けが必要です。たとえばCではコンパイル時と実行時の間にはぶ厚いカーテンが引かれているので普通は無理です。 + +Rubyではオブジェクトが自らの持つメソッドを実行時に調べることができます。これをイントロスペクション(リフレクションや自己反映計算などとも言う)と呼びます。たとえばPacket Inメッセージが到着したとき、コントローラはイントロスペクションして自分が `packet_in` メソッドを持っているかどうかを実行時に調べます。そしてもし見つかればそのメソッドを呼ぶというわけです。 + +この仕組みは `Trema::Controller` クラスを継承したときに自動的にコントローラへ導入されます。 +==== + +=== キーワード + +Rubyにはたくさんの組込みの語があり、それぞれに意味が与えられています。これらの語を変数として使ったり、自分の目的に合わせて意味を変えたりはできません。 + +---- +alias and BEGIN begin break case class def defined do else elsif END +end ensure false for if in module next nil not or redo rescue retry +return self super then true undef unless until when while yield +---- + +このうち、「Hello, Trema!」では `class` と `def` そして `end` キーワードを使いました。先ほど説明したように、`class` キーワードは続く名前 (`HelloTrema`) のクラスを定義します。`def` キーワードは続く名前(`start`) のメソッドを定義します。 + +この `def` や `class` で始まって `end` で終わる領域のことをブロックと呼びます。すべてのRubyプログラムはこのブロックがいくつか組み合わさったものです。 + +=== スイッチの起動を捕捉する + +新しくスイッチが起動すると `switch_ready` メソッドが起動します。 + +[source,ruby,subs="verbatim,attributes"] +---- +def switch_ready(dpid) + logger.info "Hello #{dpid.to_hex}!" +end +---- + +// TODO: 可能であればhello_tremaのソースコードから部分的にimportする + +`switch_ready` メソッドでは、接続したスイッチのDatapath IDを16進形式(0xで始まる文字列)でログに出力します。 + +[NOTE] +.switch_readyの中身 +==== +実は OpenFlow の仕様には `switch_ready` というメッセージは定義されていません。実は、これは Trema が独自に定義するイベントなのです。`switch_ready` の裏では<>に示す一連の複雑な処理が行われていて、Trema がこの詳細をうまくカーペットの裏に隠してくれているというわけです。 + +[[switch_ready]] +image::switch_ready.png[caption="図2-b",title="switch_ready イベントが起こるまで"] + +最初に、スイッチとコントローラがしゃべる OpenFlow プロトコルが合っているか確認します。OpenFlow の Hello メッセージを使ってお互いにプロトコルのバージョンを知らせ、うまく会話できそうか判断します。 + +次は、スイッチを識別するための Datapath ID の取得です。Datapath IDのようなスイッチ固有の情報は、スイッチに対して OpenFlow の Features Request メッセージを送ることで取得できます。成功した場合、Datapath IDやポート数などの情報が Features Reply メッセージに乗ってやってきます。 + +最後にスイッチを初期化します。スイッチに以前の状態が残っているとコントローラが管理する情報と競合が起こるので、スイッチを初期化することでこれを避けます。 + +これら一連の処理が終わると、ようやく `switch_ready` がコントローラに通知されるというわけです。 +==== + +==== Datapath IDを16進形式にする + +`to_hex` は整数を16進形式の文字列に変換するメソッドです。`switch_ready` ハンドラの引数 `dpid` の値は64ビットの正の整数で、OpenFlowでは慣習的に `0xfffb` などと16進で表します。ターミナルやログに出力する場合には `to_hex` で16進形式に変換しておいたほうがよいでしょう。 + +==== ログメッセージを出力する + +ログメッセージはログファイルに記録されます。コントローラをフォアグラウンドで実行する場合、つまり `trema run` に `--daemonize` または `-d` オプションを付けない場合にはターミナルにもログメッセージが出力されます。 + +ログメッセージを出力するには、`logger` を使います。 + +[source,ruby,subs="verbatim,attributes"] +---- +def start(_args) + logger.info 'Trema started.' +end +---- + +`logger` はTrema標準のロガーで、ログメッセージの出力はこれを通じて行います。ログメッセージの重要度に応じて、`critical` (重要度 最高)から `debug` (重要度 最低)までの次の6種類のメソッドを選べます。 + +- `critical`: 回復不能なエラー +- `error`: エラー +- `warn`: 警告 +- `notice`: 注意が必要な情報 +- `info`: 通常レベルの情報 +- `debug`: デバッグ出力 + +`trema run` のオプションでロギングレベルを指定できます。たとえば次のコードを実行するとしましょう。 + +[source,ruby,subs="verbatim,attributes"] +.try_logging.rb +---- +# ロギングレベルの確認用コード +class TryLogging < Trema::Controller + def start(_args) + logger.critical 'CRITICAL' + logger.error 'ERROR' + logger.warn 'WARN' + logger.notice 'NOTICE' + logger.info 'INFO' + logger.debug 'DEBUG' + end +end +---- + +// TODO コードは別ファイルにして、rubocopにかける + +このコードをたとえば次のようにロギングレベル `notice` で実行すると、`info` と `debug` メッセージは出力されません。 + +---- +$ ./bin/trema run try_logging.rb --logging_level notice +CRITICAL +ERROR +WARN +NOTICE +---- + +ログファイルのデフォルトパスは `/tmp/[コントローラのクラス名].log` です。たとえばHelloTremaの場合には `/tmp/HelloTrema.log` になります。ログファイルの出力先ディレクトリを変更するには、`trema run` の `--log_dir` または `-L` オプションを指定します。たとえば次のようにすると、`/var/log/HelloTrema.log` が作られます。 + +---- +$ ./bin/trema run try_logging.rb --log_dir /var/log/ +---- + +// TODO -v, --verbose の説明 + +==== 文字列を連結する + +`logger.info` に渡している文字列中の `#{}` は、文字列内にRubyの式を組込みます。 + +[source,ruby,subs="verbatim,attributes"] +---- +logger.info "Hello #{dpid.to_hex}!" +#=> Hello 0xabc! +---- + +これは次のコードと同じです。 + +[source,ruby,subs="verbatim,attributes"] +---- +logger.info 'Hello ' + dpid.to_hex + '!' +#=> Hello 0xabc! +---- + +どちらを使ってもかまいませんが、文字列を `+` でつなげすぎると最終的な出力がコードからはわかりにくくなることがあります。その場合、このように `#{}` で組み込んだほうがよいでしょう。 + +これで「Hello, Trema!」の説明はおしまいです。Tremaで作るコントローラは基本的にこの「Hello, Trema!」と同じ構成をしています。つまり、これをベースにいくつか必要なハンドラメソッドを追加していけば、より複雑で実践的なコントローラも作れます。 + +== まとめ + +この章ではTremaの開発環境をセットアップし、すべてのコントローラのテンプレートとなる「Hello, Trema!」コントローラを書きました。この章で学んだことを簡単にまとめてから、より実用的なコントローラの開発に入っていくことにしましょう。 + +- コントローラはクラスとして定義し、`Trema::Controller` クラスを継承することでコントローラの基本機能を取り込む +- コントローラに機能を追加するには、各種イベントに対応するハンドラをコントローラクラスに定義する +- コントローラは `trema run` コマンドでコンパイルなしにすぐ実行できる +- 仮想ネットワーク機能を使うと、OpenFlowスイッチを持っていなくてもコントローラを実行できる + +// TODO 新しく学んだことを確認してリストを更新 + +これでTremaの基礎知識は充分身に付きました。次の章では、OpenFlowコントローラのためのマイクロベンチマークツール、cbenchを計測するためのコントローラを書きます。 + +== 参考文献 + +Rubyプログラミングが初めてという人達のために、この章では入門に役立つサイトや本をいくつか紹介します。 + +- 「Why’s (Poignant) Guide to Ruby」(http://mislav.uniqpath.com/poignant-guide/) +
筆者は大学や職場でいろいろなプログラミング言語を勉強してきましたが、これほど読んでいて楽しい本に出会ったことはありません。この本はRuby会の謎の人物_why氏による風変わりなRuby入門で、プログラミング言語の解説書にもかかわらずまるで小説やマンガのようにリラックスして読めます。この章のRubyの品詞の説明は、この本を参考にしました(日本語版はhttp://www.aoky.net/articles/why_poignant_guide_to_ruby/) +- 「TryRuby」(http://tryruby.org/) +
同じく_why氏による、ブラウザで動くRuby環境です。Rubyを試してみたいけどインストールするのが面倒という人は、まずはここでRubyを試してみましょう。`help` と打つと15分の短いRubyチュートリアルが始まります。 +- 『プログラミングRuby第2版』(Dave Thomas、Chad Fowler、Andrew Hunt著/田和勝、まつもとゆきひろ 訳/オーム社)
 +Rubyの完全なリファレンスです。本気でRubyを勉強したい人は持っていて損はしません。リファレンスが必要ならこの本だけあれば十分です。 + +// TODO この本を読む人はRubyをインストールすること前提だから、TryRubyはちょっと違うと思う。あといきなり\_whyの本を紹介するのもめちゃくちゃなので、最初は無難な本を紹介するのがいいと思う +// TODO asciidoc のbib形式に変換 diff --git a/en/images/cloud/auth_done.jpg b/images/cloud/auth_done.jpg similarity index 100% rename from en/images/cloud/auth_done.jpg rename to images/cloud/auth_done.jpg diff --git a/en/images/cloud/dilbert.jpg b/images/cloud/dilbert.jpg similarity index 100% rename from en/images/cloud/dilbert.jpg rename to images/cloud/dilbert.jpg diff --git a/en/images/cloud/honeywell_kitchen_computer.jpg b/images/cloud/honeywell_kitchen_computer.jpg similarity index 100% rename from en/images/cloud/honeywell_kitchen_computer.jpg rename to images/cloud/honeywell_kitchen_computer.jpg diff --git a/en/images/cloud/oauth.jpg b/images/cloud/oauth.jpg similarity index 100% rename from en/images/cloud/oauth.jpg rename to images/cloud/oauth.jpg diff --git a/en/images/cloud/shindan_maker.png b/images/cloud/shindan_maker.png similarity index 100% rename from en/images/cloud/shindan_maker.png rename to images/cloud/shindan_maker.png diff --git a/en/images/cloud/transparent_proxy.jpg b/images/cloud/transparent_proxy.jpg similarity index 100% rename from en/images/cloud/transparent_proxy.jpg rename to images/cloud/transparent_proxy.jpg diff --git a/en/images/cloud/uid_mac.jpg b/images/cloud/uid_mac.jpg similarity index 100% rename from en/images/cloud/uid_mac.jpg rename to images/cloud/uid_mac.jpg diff --git a/en/images/datacenter_wakame/arp_block.png b/images/datacenter_wakame/arp_block.png similarity index 100% rename from en/images/datacenter_wakame/arp_block.png rename to images/datacenter_wakame/arp_block.png diff --git a/en/images/datacenter_wakame/arp_entangle.png b/images/datacenter_wakame/arp_entangle.png similarity index 100% rename from en/images/datacenter_wakame/arp_entangle.png rename to images/datacenter_wakame/arp_entangle.png diff --git a/en/images/datacenter_wakame/edge_network_virtualization.png b/images/datacenter_wakame/edge_network_virtualization.png similarity index 100% rename from en/images/datacenter_wakame/edge_network_virtualization.png rename to images/datacenter_wakame/edge_network_virtualization.png diff --git a/en/images/datacenter_wakame/translate.png b/images/datacenter_wakame/translate.png similarity index 100% rename from en/images/datacenter_wakame/translate.png rename to images/datacenter_wakame/translate.png diff --git a/en/images/datacenter_wakame/unicast.png b/images/datacenter_wakame/unicast.png similarity index 100% rename from en/images/datacenter_wakame/unicast.png rename to images/datacenter_wakame/unicast.png diff --git a/en/images/datacenter_wakame/wakame_overview.png b/images/datacenter_wakame/wakame_overview.png similarity index 100% rename from en/images/datacenter_wakame/wakame_overview.png rename to images/datacenter_wakame/wakame_overview.png diff --git a/en/images/datacenter_wakame/wakame_screenshot.png b/images/datacenter_wakame/wakame_screenshot.png similarity index 100% rename from en/images/datacenter_wakame/wakame_screenshot.png rename to images/datacenter_wakame/wakame_screenshot.png diff --git a/en/images/diy_switch/akihabara.jpg b/images/diy_switch/akihabara.jpg similarity index 100% rename from en/images/diy_switch/akihabara.jpg rename to images/diy_switch/akihabara.jpg diff --git a/en/images/diy_switch/diy_switch_network.png b/images/diy_switch/diy_switch_network.png similarity index 100% rename from en/images/diy_switch/diy_switch_network.png rename to images/diy_switch/diy_switch_network.png diff --git a/en/images/diy_switch/firmware_update_setup.png b/images/diy_switch/firmware_update_setup.png similarity index 100% rename from en/images/diy_switch/firmware_update_setup.png rename to images/diy_switch/firmware_update_setup.png diff --git a/en/images/diy_switch/orc_srchack.jpg b/images/diy_switch/orc_srchack.jpg similarity index 100% rename from en/images/diy_switch/orc_srchack.jpg rename to images/diy_switch/orc_srchack.jpg diff --git a/en/images/diy_switch/switch_internal_vlan.png b/images/diy_switch/switch_internal_vlan.png similarity index 100% rename from en/images/diy_switch/switch_internal_vlan.png rename to images/diy_switch/switch_internal_vlan.png diff --git a/en/images/diy_switch/trema_interop.jpg b/images/diy_switch/trema_interop.jpg similarity index 100% rename from en/images/diy_switch/trema_interop.jpg rename to images/diy_switch/trema_interop.jpg diff --git a/en/images/diy_switch/update.png b/images/diy_switch/update.png similarity index 100% rename from en/images/diy_switch/update.png rename to images/diy_switch/update.png diff --git a/en/images/google/bgp.png b/images/google/bgp.png similarity index 100% rename from en/images/google/bgp.png rename to images/google/bgp.png diff --git a/en/images/google/conflict.png b/images/google/conflict.png similarity index 100% rename from en/images/google/conflict.png rename to images/google/conflict.png diff --git a/en/images/google/multipath.png b/images/google/multipath.png similarity index 100% rename from en/images/google/multipath.png rename to images/google/multipath.png diff --git a/en/images/google/te_server.png b/images/google/te_server.png similarity index 100% rename from en/images/google/te_server.png rename to images/google/te_server.png diff --git a/en/images/google/traffic.png b/images/google/traffic.png similarity index 100% rename from en/images/google/traffic.png rename to images/google/traffic.png diff --git a/en/images/google/wan.png b/images/google/wan.png similarity index 100% rename from en/images/google/wan.png rename to images/google/wan.png diff --git a/en/images/google/wan_equip.png b/images/google/wan_equip.png similarity index 100% rename from en/images/google/wan_equip.png rename to images/google/wan_equip.png diff --git a/en/images/learning_switch/host1to2_flood_openflow.png b/images/learning_switch/host1to2_flood_openflow.png similarity index 100% rename from en/images/learning_switch/host1to2_flood_openflow.png rename to images/learning_switch/host1to2_flood_openflow.png diff --git a/en/images/learning_switch/host1to2_openflow.png b/images/learning_switch/host1to2_openflow.png similarity index 100% rename from en/images/learning_switch/host1to2_openflow.png rename to images/learning_switch/host1to2_openflow.png diff --git a/en/images/learning_switch/host2to1.png b/images/learning_switch/host2to1.png similarity index 100% rename from en/images/learning_switch/host2to1.png rename to images/learning_switch/host2to1.png diff --git a/en/images/learning_switch/host2to1_openflow.png b/images/learning_switch/host2to1_openflow.png similarity index 100% rename from en/images/learning_switch/host2to1_openflow.png rename to images/learning_switch/host2to1_openflow.png diff --git a/en/images/learning_switch/learn.png b/images/learning_switch/learn.png similarity index 100% rename from en/images/learning_switch/learn.png rename to images/learning_switch/learn.png diff --git a/en/images/learning_switch/switch_network.png b/images/learning_switch/switch_network.png similarity index 100% rename from en/images/learning_switch/switch_network.png rename to images/learning_switch/switch_network.png diff --git a/en/images/learning_switch/switch_network_openflow.png b/images/learning_switch/switch_network_openflow.png similarity index 100% rename from en/images/learning_switch/switch_network_openflow.png rename to images/learning_switch/switch_network_openflow.png diff --git a/en/images/migrating_openflow/bicycle.png b/images/migrating_openflow/bicycle.png similarity index 100% rename from en/images/migrating_openflow/bicycle.png rename to images/migrating_openflow/bicycle.png diff --git a/en/images/migrating_openflow/buggy_controller_setup.png b/images/migrating_openflow/buggy_controller_setup.png similarity index 100% rename from en/images/migrating_openflow/buggy_controller_setup.png rename to images/migrating_openflow/buggy_controller_setup.png diff --git a/en/images/migrating_openflow/failure_analysis.png b/images/migrating_openflow/failure_analysis.png similarity index 100% rename from en/images/migrating_openflow/failure_analysis.png rename to images/migrating_openflow/failure_analysis.png diff --git a/en/images/migrating_openflow/oneway_bridge.png b/images/migrating_openflow/oneway_bridge.png similarity index 100% rename from en/images/migrating_openflow/oneway_bridge.png rename to images/migrating_openflow/oneway_bridge.png diff --git a/en/images/migrating_openflow/pattern1.png b/images/migrating_openflow/pattern1.png similarity index 100% rename from en/images/migrating_openflow/pattern1.png rename to images/migrating_openflow/pattern1.png diff --git a/en/images/migrating_openflow/pattern2.png b/images/migrating_openflow/pattern2.png similarity index 100% rename from en/images/migrating_openflow/pattern2.png rename to images/migrating_openflow/pattern2.png diff --git a/en/images/migrating_openflow/pattern3.png b/images/migrating_openflow/pattern3.png similarity index 100% rename from en/images/migrating_openflow/pattern3.png rename to images/migrating_openflow/pattern3.png diff --git a/en/images/openflow_framework_trema/ccrb.jpg b/images/openflow_framework_trema/ccrb.jpg similarity index 100% rename from en/images/openflow_framework_trema/ccrb.jpg rename to images/openflow_framework_trema/ccrb.jpg diff --git a/en/images/openflow_framework_trema/izakaya.png b/images/openflow_framework_trema/izakaya.png similarity index 100% rename from en/images/openflow_framework_trema/izakaya.png rename to images/openflow_framework_trema/izakaya.png diff --git a/images/openflow_framework_trema/switch_ready.png b/images/openflow_framework_trema/switch_ready.png new file mode 100644 index 00000000..10c9d77e Binary files /dev/null and b/images/openflow_framework_trema/switch_ready.png differ diff --git a/en/images/openflow_framework_trema/trema_logo.png b/images/openflow_framework_trema/trema_logo.png similarity index 100% rename from en/images/openflow_framework_trema/trema_logo.png rename to images/openflow_framework_trema/trema_logo.png diff --git a/images/openflow_framework_trema/trema_overview.png b/images/openflow_framework_trema/trema_overview.png new file mode 100644 index 00000000..34aa045a Binary files /dev/null and b/images/openflow_framework_trema/trema_overview.png differ diff --git a/en/images/openflow_framework_trema/trema_ruby.png b/images/openflow_framework_trema/trema_ruby.png similarity index 100% rename from en/images/openflow_framework_trema/trema_ruby.png rename to images/openflow_framework_trema/trema_ruby.png diff --git a/en/images/openflow_frameworks/comparison.png b/images/openflow_frameworks/comparison.png similarity index 100% rename from en/images/openflow_frameworks/comparison.png rename to images/openflow_frameworks/comparison.png diff --git a/en/images/openflow_frameworks/floodlight.png b/images/openflow_frameworks/floodlight.png similarity index 100% rename from en/images/openflow_frameworks/floodlight.png rename to images/openflow_frameworks/floodlight.png diff --git a/en/images/openflow_frameworks/nox.png b/images/openflow_frameworks/nox.png similarity index 100% rename from en/images/openflow_frameworks/nox.png rename to images/openflow_frameworks/nox.png diff --git a/en/images/openflow_frameworks/pox.png b/images/openflow_frameworks/pox.png similarity index 100% rename from en/images/openflow_frameworks/pox.png rename to images/openflow_frameworks/pox.png diff --git a/en/images/openflow_frameworks/robot.png b/images/openflow_frameworks/robot.png similarity index 100% rename from en/images/openflow_frameworks/robot.png rename to images/openflow_frameworks/robot.png diff --git a/en/images/openflow_frameworks/trema.png b/images/openflow_frameworks/trema.png similarity index 100% rename from en/images/openflow_frameworks/trema.png rename to images/openflow_frameworks/trema.png diff --git a/en/images/openflow_spec/buffer_id.png b/images/openflow_spec/buffer_id.png similarity index 100% rename from en/images/openflow_spec/buffer_id.png rename to images/openflow_spec/buffer_id.png diff --git a/en/images/openflow_spec/features_request_reply.png b/images/openflow_spec/features_request_reply.png similarity index 100% rename from en/images/openflow_spec/features_request_reply.png rename to images/openflow_spec/features_request_reply.png diff --git a/en/images/openflow_spec/flow_removed.png b/images/openflow_spec/flow_removed.png similarity index 100% rename from en/images/openflow_spec/flow_removed.png rename to images/openflow_spec/flow_removed.png diff --git a/en/images/openflow_spec/flowmod_packetout.png b/images/openflow_spec/flowmod_packetout.png similarity index 100% rename from en/images/openflow_spec/flowmod_packetout.png rename to images/openflow_spec/flowmod_packetout.png diff --git a/en/images/openflow_spec/packet_in.png b/images/openflow_spec/packet_in.png similarity index 100% rename from en/images/openflow_spec/packet_in.png rename to images/openflow_spec/packet_in.png diff --git a/en/images/openflow_spec/rewrite_ip_address.png b/images/openflow_spec/rewrite_ip_address.png similarity index 100% rename from en/images/openflow_spec/rewrite_ip_address.png rename to images/openflow_spec/rewrite_ip_address.png diff --git a/en/images/openflow_spec/rewrite_mac.png b/images/openflow_spec/rewrite_mac.png similarity index 100% rename from en/images/openflow_spec/rewrite_mac.png rename to images/openflow_spec/rewrite_mac.png diff --git a/en/images/openflow_spec/rewrite_port.png b/images/openflow_spec/rewrite_port.png similarity index 100% rename from en/images/openflow_spec/rewrite_port.png rename to images/openflow_spec/rewrite_port.png diff --git a/en/images/openflow_spec/secure_channel.png b/images/openflow_spec/secure_channel.png similarity index 100% rename from en/images/openflow_spec/secure_channel.png rename to images/openflow_spec/secure_channel.png diff --git a/en/images/openflow_spec/strip_vlan.png b/images/openflow_spec/strip_vlan.png similarity index 100% rename from en/images/openflow_spec/strip_vlan.png rename to images/openflow_spec/strip_vlan.png diff --git a/en/images/openflow_spec/torema.png b/images/openflow_spec/torema.png similarity index 100% rename from en/images/openflow_spec/torema.png rename to images/openflow_spec/torema.png diff --git a/en/images/openflow_spec/version_negotiation.png b/images/openflow_spec/version_negotiation.png similarity index 100% rename from en/images/openflow_spec/version_negotiation.png rename to images/openflow_spec/version_negotiation.png diff --git a/en/images/openflow_usecases/advanced_load_balancer.png b/images/openflow_usecases/advanced_load_balancer.png similarity index 100% rename from en/images/openflow_usecases/advanced_load_balancer.png rename to images/openflow_usecases/advanced_load_balancer.png diff --git a/en/images/openflow_usecases/fail_over.png b/images/openflow_usecases/fail_over.png similarity index 100% rename from en/images/openflow_usecases/fail_over.png rename to images/openflow_usecases/fail_over.png diff --git a/en/images/openflow_usecases/load_balancer.png b/images/openflow_usecases/load_balancer.png similarity index 100% rename from en/images/openflow_usecases/load_balancer.png rename to images/openflow_usecases/load_balancer.png diff --git a/en/images/openflow_usecases/maximize_bandwidth.png b/images/openflow_usecases/maximize_bandwidth.png similarity index 100% rename from en/images/openflow_usecases/maximize_bandwidth.png rename to images/openflow_usecases/maximize_bandwidth.png diff --git a/en/images/openflow_usecases/multicast.png b/images/openflow_usecases/multicast.png similarity index 100% rename from en/images/openflow_usecases/multicast.png rename to images/openflow_usecases/multicast.png diff --git a/en/images/openflow_usecases/patch_panel.png b/images/openflow_usecases/patch_panel.png similarity index 100% rename from en/images/openflow_usecases/patch_panel.png rename to images/openflow_usecases/patch_panel.png diff --git a/en/images/openflow_usecases/pipeline.png b/images/openflow_usecases/pipeline.png similarity index 100% rename from en/images/openflow_usecases/pipeline.png rename to images/openflow_usecases/pipeline.png diff --git a/en/images/openflow_usecases/router.png b/images/openflow_usecases/router.png similarity index 100% rename from en/images/openflow_usecases/router.png rename to images/openflow_usecases/router.png diff --git a/en/images/openflow_usecases/switch.png b/images/openflow_usecases/switch.png similarity index 100% rename from en/images/openflow_usecases/switch.png rename to images/openflow_usecases/switch.png diff --git a/en/images/openflow_usecases/traffic_switch.png b/images/openflow_usecases/traffic_switch.png similarity index 100% rename from en/images/openflow_usecases/traffic_switch.png rename to images/openflow_usecases/traffic_switch.png diff --git a/en/images/patch_panel/cables.png b/images/patch_panel/cables.png similarity index 100% rename from en/images/patch_panel/cables.png rename to images/patch_panel/cables.png diff --git a/en/images/patch_panel/openflow_patch_panel.png b/images/patch_panel/openflow_patch_panel.png similarity index 100% rename from en/images/patch_panel/openflow_patch_panel.png rename to images/patch_panel/openflow_patch_panel.png diff --git a/en/images/patch_panel/patch_panel.png b/images/patch_panel/patch_panel.png similarity index 100% rename from en/images/patch_panel/patch_panel.png rename to images/patch_panel/patch_panel.png diff --git a/en/images/router_part1/arp_reply.png b/images/router_part1/arp_reply.png similarity index 100% rename from en/images/router_part1/arp_reply.png rename to images/router_part1/arp_reply.png diff --git a/en/images/router_part1/arp_request.png b/images/router_part1/arp_request.png similarity index 100% rename from en/images/router_part1/arp_request.png rename to images/router_part1/arp_request.png diff --git a/en/images/router_part1/forward.png b/images/router_part1/forward.png similarity index 100% rename from en/images/router_part1/forward.png rename to images/router_part1/forward.png diff --git a/en/images/router_part1/map.png b/images/router_part1/map.png similarity index 100% rename from en/images/router_part1/map.png rename to images/router_part1/map.png diff --git a/en/images/router_part1/router_network.png b/images/router_part1/router_network.png similarity index 100% rename from en/images/router_part1/router_network.png rename to images/router_part1/router_network.png diff --git a/en/images/router_part2/aggregate.png b/images/router_part2/aggregate.png similarity index 100% rename from en/images/router_part2/aggregate.png rename to images/router_part2/aggregate.png diff --git a/en/images/router_part2/default_route.png b/images/router_part2/default_route.png similarity index 100% rename from en/images/router_part2/default_route.png rename to images/router_part2/default_route.png diff --git a/en/images/router_part2/longest_match.png b/images/router_part2/longest_match.png similarity index 100% rename from en/images/router_part2/longest_match.png rename to images/router_part2/longest_match.png diff --git a/en/images/router_part2/map.png b/images/router_part2/map.png similarity index 100% rename from en/images/router_part2/map.png rename to images/router_part2/map.png diff --git a/en/images/router_part2/network.png b/images/router_part2/network.png similarity index 100% rename from en/images/router_part2/network.png rename to images/router_part2/network.png diff --git a/en/images/router_part2/router_address.png b/images/router_part2/router_address.png similarity index 100% rename from en/images/router_part2/router_address.png rename to images/router_part2/router_address.png diff --git a/en/images/router_part2/router_network.png b/images/router_part2/router_network.png similarity index 100% rename from en/images/router_part2/router_network.png rename to images/router_part2/router_network.png diff --git a/en/images/routing_switch/dijkstra.png b/images/routing_switch/dijkstra.png similarity index 100% rename from en/images/routing_switch/dijkstra.png rename to images/routing_switch/dijkstra.png diff --git a/en/images/routing_switch/flow_mod.png b/images/routing_switch/flow_mod.png similarity index 100% rename from en/images/routing_switch/flow_mod.png rename to images/routing_switch/flow_mod.png diff --git a/en/images/routing_switch/fullmesh.png b/images/routing_switch/fullmesh.png similarity index 100% rename from en/images/routing_switch/fullmesh.png rename to images/routing_switch/fullmesh.png diff --git a/en/images/routing_switch/lldp-packet-in.png b/images/routing_switch/lldp-packet-in.png similarity index 100% rename from en/images/routing_switch/lldp-packet-in.png rename to images/routing_switch/lldp-packet-in.png diff --git a/en/images/routing_switch/lldp_openflow.png b/images/routing_switch/lldp_openflow.png similarity index 100% rename from en/images/routing_switch/lldp_openflow.png rename to images/routing_switch/lldp_openflow.png diff --git a/en/images/routing_switch/lldp_overview.png b/images/routing_switch/lldp_overview.png similarity index 100% rename from en/images/routing_switch/lldp_overview.png rename to images/routing_switch/lldp_overview.png diff --git a/en/images/routing_switch/multipath.png b/images/routing_switch/multipath.png similarity index 100% rename from en/images/routing_switch/multipath.png rename to images/routing_switch/multipath.png diff --git a/en/images/routing_switch/routing_switch.png b/images/routing_switch/routing_switch.png similarity index 100% rename from en/images/routing_switch/routing_switch.png rename to images/routing_switch/routing_switch.png diff --git a/en/images/routing_switch/shownet_topology.png b/images/routing_switch/shownet_topology.png similarity index 100% rename from en/images/routing_switch/shownet_topology.png rename to images/routing_switch/shownet_topology.png diff --git a/en/images/routing_switch/spt1.png b/images/routing_switch/spt1.png similarity index 100% rename from en/images/routing_switch/spt1.png rename to images/routing_switch/spt1.png diff --git a/en/images/routing_switch/spt2.png b/images/routing_switch/spt2.png similarity index 100% rename from en/images/routing_switch/spt2.png rename to images/routing_switch/spt2.png diff --git a/en/images/routing_switch/topology_after.png b/images/routing_switch/topology_after.png similarity index 100% rename from en/images/routing_switch/topology_after.png rename to images/routing_switch/topology_after.png diff --git a/en/images/routing_switch/topology_before.png b/images/routing_switch/topology_before.png similarity index 100% rename from en/images/routing_switch/topology_before.png rename to images/routing_switch/topology_before.png diff --git a/en/images/sliceable_switch/creating_slices.png b/images/sliceable_switch/creating_slices.png similarity index 100% rename from en/images/sliceable_switch/creating_slices.png rename to images/sliceable_switch/creating_slices.png diff --git a/en/images/sliceable_switch/rest_overview.png b/images/sliceable_switch/rest_overview.png similarity index 100% rename from en/images/sliceable_switch/rest_overview.png rename to images/sliceable_switch/rest_overview.png diff --git a/en/images/sliceable_switch/slice.png b/images/sliceable_switch/slice.png similarity index 100% rename from en/images/sliceable_switch/slice.png rename to images/sliceable_switch/slice.png diff --git a/en/images/sliceable_switch/sliceable_switch_internals.png b/images/sliceable_switch/sliceable_switch_internals.png similarity index 100% rename from en/images/sliceable_switch/sliceable_switch_internals.png rename to images/sliceable_switch/sliceable_switch_internals.png diff --git a/en/images/sliceable_switch/sliceable_switch_network.png b/images/sliceable_switch/sliceable_switch_network.png similarity index 100% rename from en/images/sliceable_switch/sliceable_switch_network.png rename to images/sliceable_switch/sliceable_switch_network.png diff --git a/en/images/sliceable_switch/sliceable_switch_overview.png b/images/sliceable_switch/sliceable_switch_overview.png similarity index 100% rename from en/images/sliceable_switch/sliceable_switch_overview.png rename to images/sliceable_switch/sliceable_switch_overview.png diff --git a/en/images/switch_monitoring_tool/scope.png b/images/switch_monitoring_tool/scope.png similarity index 100% rename from en/images/switch_monitoring_tool/scope.png rename to images/switch_monitoring_tool/scope.png diff --git a/en/images/switch_monitoring_tool/switch_monitor_overview.png b/images/switch_monitoring_tool/switch_monitor_overview.png similarity index 100% rename from en/images/switch_monitoring_tool/switch_monitor_overview.png rename to images/switch_monitoring_tool/switch_monitor_overview.png diff --git a/en/images/tdd/repeater_hub.png b/images/tdd/repeater_hub.png similarity index 100% rename from en/images/tdd/repeater_hub.png rename to images/tdd/repeater_hub.png diff --git a/en/images/tdd/repeater_hub_openflow.png b/images/tdd/repeater_hub_openflow.png similarity index 100% rename from en/images/tdd/repeater_hub_openflow.png rename to images/tdd/repeater_hub_openflow.png diff --git a/en/images/tdd/yutaro_test.png b/images/tdd/yutaro_test.png similarity index 100% rename from en/images/tdd/yutaro_test.png rename to images/tdd/yutaro_test.png diff --git a/en/images/traffic_monitor/flags.png b/images/traffic_monitor/flags.png similarity index 100% rename from en/images/traffic_monitor/flags.png rename to images/traffic_monitor/flags.png diff --git a/en/images/traffic_monitor/flow_removed.png b/images/traffic_monitor/flow_removed.png similarity index 100% rename from en/images/traffic_monitor/flow_removed.png rename to images/traffic_monitor/flow_removed.png diff --git a/en/images/traffic_monitor/packet_in.png b/images/traffic_monitor/packet_in.png similarity index 100% rename from en/images/traffic_monitor/packet_in.png rename to images/traffic_monitor/packet_in.png diff --git a/en/images/traffic_monitor/traffic_monitor_setup.png b/images/traffic_monitor/traffic_monitor_setup.png similarity index 100% rename from en/images/traffic_monitor/traffic_monitor_setup.png rename to images/traffic_monitor/traffic_monitor_setup.png diff --git a/en/images/trema_architecture/flow_cookie_virtualization.png b/images/trema_architecture/flow_cookie_virtualization.png similarity index 100% rename from en/images/trema_architecture/flow_cookie_virtualization.png rename to images/trema_architecture/flow_cookie_virtualization.png diff --git a/en/images/trema_architecture/switch_daemon.png b/images/trema_architecture/switch_daemon.png similarity index 100% rename from en/images/trema_architecture/switch_daemon.png rename to images/trema_architecture/switch_daemon.png diff --git a/en/images/trema_architecture/switch_manager_daemon.png b/images/trema_architecture/switch_manager_daemon.png similarity index 100% rename from en/images/trema_architecture/switch_manager_daemon.png rename to images/trema_architecture/switch_manager_daemon.png diff --git a/en/images/trema_architecture/switch_virtualization.png b/images/trema_architecture/switch_virtualization.png similarity index 100% rename from en/images/trema_architecture/switch_virtualization.png rename to images/trema_architecture/switch_virtualization.png diff --git a/en/images/trema_architecture/trema_internal_with_tremashark.png b/images/trema_architecture/trema_internal_with_tremashark.png similarity index 100% rename from en/images/trema_architecture/trema_internal_with_tremashark.png rename to images/trema_architecture/trema_internal_with_tremashark.png diff --git a/en/images/trema_architecture/tremashark_gui.png b/images/trema_architecture/tremashark_gui.png similarity index 100% rename from en/images/trema_architecture/tremashark_gui.png rename to images/trema_architecture/tremashark_gui.png diff --git a/en/images/trema_architecture/tremashark_overview.png b/images/trema_architecture/tremashark_overview.png similarity index 100% rename from en/images/trema_architecture/tremashark_overview.png rename to images/trema_architecture/tremashark_overview.png diff --git a/en/images/trema_architecture/yonaka.png b/images/trema_architecture/yonaka.png similarity index 100% rename from en/images/trema_architecture/yonaka.png rename to images/trema_architecture/yonaka.png diff --git a/en/images/whats_openflow/incredible_machine.png b/images/whats_openflow/incredible_machine.png similarity index 100% rename from en/images/whats_openflow/incredible_machine.png rename to images/whats_openflow/incredible_machine.png diff --git a/en/images/whats_openflow/openflow_host_switch.png b/images/whats_openflow/openflow_host_switch.png similarity index 100% rename from en/images/whats_openflow/openflow_host_switch.png rename to images/whats_openflow/openflow_host_switch.png diff --git a/en/images/whats_openflow/openflow_host_switch_controller.png b/images/whats_openflow/openflow_host_switch_controller.png similarity index 100% rename from en/images/whats_openflow/openflow_host_switch_controller.png rename to images/whats_openflow/openflow_host_switch_controller.png diff --git a/en/images/whats_openflow/yoyodyne_support.png b/images/whats_openflow/yoyodyne_support.png similarity index 100% rename from en/images/whats_openflow/yoyodyne_support.png rename to images/whats_openflow/yoyodyne_support.png diff --git a/en/images/whats_openflow/yoyodyne_support_miyasaka.png b/images/whats_openflow/yoyodyne_support_miyasaka.png similarity index 100% rename from en/images/whats_openflow/yoyodyne_support_miyasaka.png rename to images/whats_openflow/yoyodyne_support_miyasaka.png diff --git a/index.adoc b/index.adoc new file mode 100644 index 00000000..03a9f262 --- /dev/null +++ b/index.adoc @@ -0,0 +1,5 @@ += Trema本 + +:leveloffset: 1 + +include::book.adoc[] diff --git a/ja/README.md b/ja/README.md new file mode 100644 index 00000000..f89f74dc --- /dev/null +++ b/ja/README.md @@ -0,0 +1,2 @@ +ここにあるのは第1版の内容です. 書き中の第2版についてはトップディレクト +リの *.org を見てください. diff --git a/ja/sources/learning_switch/learning-switch.rb b/ja/sources/learning_switch/learning-switch.rb deleted file mode 100644 index 93146b9f..00000000 --- a/ja/sources/learning_switch/learning-switch.rb +++ /dev/null @@ -1,56 +0,0 @@ -# -# Layer-2 switch implementation in Trema. -# -class LearningSwitch < Controller - def start - @fdb = {} - end - - - def packet_in datapath_id, message - learn message.macsa, message.in_port - port_no = port_no_from( message.macda ) - if port_no - flow_mod message, port_no - packet_out message, port_no - else - flood message - end - end - - - private - - - def learn mac, port_no - @fdb[ mac ] = port_no - end - - - def port_no_from mac - @fdb[ mac ] - end - - - def flow_mod message, port_no - send_flow_mod_add( - message.datapath_id, - :match => ExactMatch.from( message ), - :actions => SendOutPort.new( port_no ) - ) - end - - - def packet_out message, port_no - send_packet_out( - message.datapath_id, - :packet_in => message, - :actions => SendOutPort.new( port_no ) - ) - end - - - def flood message - packet_out message, OFPP_FLOOD - end -end diff --git a/openflow_framework_trema.org b/openflow_framework_trema.org deleted file mode 100644 index e4837e82..00000000 --- a/openflow_framework_trema.org +++ /dev/null @@ -1,622 +0,0 @@ -#+TITLE: OpenFlow フレームワーク Trema -#+AUTHOR: Yasuhito Takamiya -#+LANGUAGE: ja -#+ICALENDAR_EXCLUDE_TAGS: noex -#+HTML_HEAD_EXTRA: -#+OPTIONS: toc:nil - -* OpenFlow フレームワーク Trema - -#+BEGIN_VERSE -@@html:Trema (トレマ) @@ を使うと楽しく OpenFlow プログラミングの世界が味わえます。これでいよいよあなたも SDN プログラマの仲間入りです! -#+END_VERSE - -#+ATTR_HTML: :width 500pt -[[./ja/images/openflow_framework_trema/izakaya.png]] - -** 作って分かる OpenFlow - -Part 2 では、いよいよ実際に OpenFlow でネットワークをプログラムする方法 -を紹介します。職場や自宅のような中小規模ネットワークでもすぐに試せる実 -用的なコードを通じて、「OpenFlow って具体的に何に使えるの?」 -「OpenFlow コントローラってどう作るの?」というよくある疑問に答えていき -ます。 - -題材はなるべく実用例を取り上げるようにし、また OpenFlow やネットワーク -の基礎から説明していくようにしました。このためネットワークの専門家はも -ちろん、普通のプログラマにもすんなり理解できる内容となっているはずです。 - -まずは、この Part 2 で使う OpenFlow プログラミングのためのフレームワー -ク、Trema をあらためて紹介します。 - -** Trema とは - -Trema は OpenFlow コントローラを開発するための Ruby 向けプログラミング -フレームワークです。GitHub 上でオープンに開発しており、GPL2 ライセンス -のフリーソフトウェアです。公開は 2011 年の 4 月と比較的新しいソフトウェ -アですが、その使いやすさから国内外の企業や大学、および研究機関などです -でに数多く採用されています。 - -Trema の情報は主に次の URL から入手できます。 - -- Trema ホームページ:http://trema.github.com/trema/ -- GitHub のプロジェクトページ:http://github.com/trema/ -- メーリングリスト:http://groups.google.com/group/trema-dev -- Twitter アカウント:http://twitter.com/trema_news - -Trema の特長をひとことで言うと、「Ruby on Rails や Sinatra などの Web -フレームワークに影響を受けた、アジャイルな OpenFlow プログラミングフレー -ムワークである」ということです。たとえば最近のアジャイル開発環境ではも -はや当たり前ですが、Trema は開発を効率化する便利なツールを数多く提供し -ています。このように開発サイクル全体の面倒を見てくれるところが Trema の -「フレームワーク」たるゆえんで、他の OpenFlow 開発ツールとはまったく違 -うところです。 - -ここからは実際にこの Trema を使って OpenFlow コントローラを作っていきま -す。まずは Trema をセットアップしましょう。 - -*** コラム @@html:友太郎 (ゆうたろう) @@の質問: Trema の由来ってなに? -:PROPERTIES: -:EXPORT_OPTIONS: num:nil -:END: - -よく「Trema の由来って何ですか?」と聞かれるのですが、これは筆者の1人が -大好きな大阪の「とれまレコード (http://www.fumiyatanaka.com/)」というレ -コードレーベルの名前からきています。テクノミュージックを中心にリリース -する小さなレーベルですが、DJ の間では世界的に知られています。 - -さてそもそもこの"とれま"とはいったい何でしょう。これは日本がバブルの頃、 -道路の「とまれ」という標示がよく「とれま」と間違えて描かれていたという -事実が元になっています。このありえない誤植の原因は、バブル景気時代に急 -増した外国人労働者が日本語もままならないまま道路工事現場で働いていたと -いうことにあるそうです。由来を探ってみると意外と面白い事実に行き着くこ -とってありますね。 - -ちなみに、Trema の公式ロゴマークは [[fig:trema_logo]] です。これは -Twitter の Trema 公式アカウント (=@trema_news=) のアイコンとしても使わ -れています。 - -#+CAPTION: Trema の公式ロゴマーク -#+LABEL: fig:trema_logo -#+ATTR_HTML: :width 400pt -[[./ja/images/openflow_framework_trema/trema_logo.png]] - -もちろん、こんなに大胆な道路標識は日本中どこを探してもありません。この -本の編集者が画像編集ソフトで試しに作ったところ評判が良かったので、その -まま公式ロゴになりました。 - -** Tremaのセットアップ - -Trema は Linux 上で動作します。次のディストリビューションとバージョンで -の動作を保証しています。 - -- Ubuntu 10.04 以降 (i386/amd64, デスクトップ版) -- Debian GNU/Linux 6.0 (i386/amd64) - -なお保証はしていませんが、RedHat などその他の Linux ディストリビューショ -ンでも動作するはずです。 - -Tremaの提供する =trema= コマンドの実行には root 権限が必要です。まずは、 -=sudo= を使って root 権限でコマンドを実行できるかどうか、 =sudo= の設定 -ファイルを確認してください。 - -=sudo= が正しく設定できていることを確認したら、Trema のインストールや実 -行に必要ないくつかのソフトウェアをインストールしましょう。 - -- Rubyインタプリタ :: - Trema は Ruby と C で作成されていて、アプリケーションの記述には - Ruby を使います。Trema では Ruby のバージョン 1.8.7 以降が必要です。 -- Ruby のパッケージ管理システム RubyGems :: - Ruby のライブラリは gem という形式で公開されています. =gem= コマ - ンドを使うと,Trema が依存する gem を簡単にインストールできます. -- Trema 本体 :: - 本書は、Trema バージョン 0.4.1 を使用して執筆しています。 -- Trema のコンパイルに必要なコンパイラおよびライブラリ :: - =gcc= や =libpcap= など,Trema の C で書かれたソースコードをコンパ - イルするのに必要なコンパイラやライブラリです. - -それでは、Ubuntu のパッケージ管理システム =apt-get= を使って必要なパッ -ケージを次のようにインストールしてください。もし他のディストリビューショ -ンを使いたい場合は、コマンド名とパッケージ名を適宜読み替えください. - -#+BEGIN_SRC -% sudo apt-get install gcc make ruby rubygems ruby-dev libpcap-dev libsqlite3-dev libglib2.0-dev -#+END_SRC - -以上で Trema をインストールするための準備が整いました。続いて Trema を -インストールするには、RubyGems のパッケージで手軽にインストールする方法 -と、最新のソースコードを取得して自分でビルドする方法があります。それぞ -れ説明していきましょう。 - -*** パッケージで手軽にインストールする場合 - -Trema は RubyGems を使って次のようにコマンド一発で簡単にインストールで -きます。 - -#+BEGIN_SRC -% gem install trema -#+END_SRC - -インストールに成功すると、自動的に Trema のコマンド =trema= にパスが通っ -ているはずです。次のコマンドでバージョンが表示されることを確認してください. - -#+BEGIN_SRC -% trema --version -trema version 0.4.1 -#+END_SRC - -*** ソースコードから最新版をインストールする場合 - -最新版をインストールしたい人は、GitHub から自分でソースコードをダウンロー -ドしてビルドすることもできます。まず、次のように =git= を使って最新のソー -スコードを取得してください。 - -#+BEGIN_SRC -% git clone git://github.com/trema/trema.git -#+END_SRC - -次のコマンドを実行すると、Trema が依存する RubyGems のパッケージが自動 -的にインストールされます。 - -#+BEGIN_SRC -% cd trema -% sudo gem install bundler -% bundle install -#+END_SRC - -次のコマンドで Trema をダウンロードしたディレクトリ以下に Trema がイン -ストールされます。 =make install= のようなシステム全体へのインストール -手順は不要です。 - -#+BEGIN_SRC -% ./build.rb -#+END_SRC - -次のコマンドで =trema= コマンドが正しくインストールされたか確認してくだ -さい。 - -#+BEGIN_SRC -% ./trema --version -trema version 0.4.1 -#+END_SRC - -もし必要あればこのディレクトリにパスを通し、 =trema= コマンドが簡単に起 -動できるようにしておいてください。 - -さあ、これで Trema による OpenFlow 開発環境が整いました。それでは早速、 -入門の定番 Hello, World を Trema で書いてみましょう。 - -** Hello, Trema! - -今から書くアプリケーションは最も簡単な OpenFlow コントローラの一種で、 -画面に「 =Hello, Trema!= 」と表示するだけのものです。スイッチとはまった -くやりとりしないスタンドアロンのアプリケーションですが、Trema で作れる -コントローラの基本がすべて入っています。 - -では、適当なディレクトリにエディタで =hello-trema.rb= というファイルを -開き、次のコードを入力してください。"=.rb=" は Ruby プログラムの標準的 -な拡張子です。なお Ruby の文法は必要に応じておいおい説明しますので、も -し分からなくても今のところは気にせずそのまま入力してください。 - -#+BEGIN_SRC ruby - class HelloTrema < Controller - def start - puts "Hello, Trema!" - end - end -#+END_SRC - -意味はまだわからないかもしれませんが、とてもシンプルに見えますね。それ -では細かい文法は後で見るとして「習うより慣れろ」でさっそく実行してみま -しょう。 - -*** 実行してみよう (=trema run=) - -作成したコントローラは =trema run= コマンドですぐに実行できます。Ruby -はインタプリタ言語なので、コンパイルの必要はありません。ターミナルで次 -のように入力すると、この世界一短い OpenFlow コントローラはフォアグラウ -ンドプロセスとして起動し、画面に「 =Hello, Trema!= 」と出力します。起動し -たコントローラは =Ctrl + c= で停止できます。 - -#+BEGIN_SRC -% trema run ./hello-trema.rb -Hello, Trema! # Ctrl+c で終了 -% -#+END_SRC - -いかがでしょうか? =trema= コマンドを使うと、とても簡単にコントローラを -実行できますね。 =trema= コマンドには他にもいくつかの機能がありますので -ここで簡単に紹介しておきましょう。 - -** =trema= コマンド - -=trema= コマンドは Trema 唯一のコマンドラインツールであり、コントローラ -の起動やテストなど様々な用途に使います。たとえば先ほどの 「Hello -Trema!」で見たように、 =trema run= はコントローラを起動するためのコマン -ドです。起動したコントローラは OpenFlow スイッチと接続しメッセージをや -りとりします。また、次の章以降で触れますが =trema run= コマンドはオプショ -ンで仮想ネットワークを作ることもでき、作ったコントローラをこの仮想ネッ -トワークの中でテストできます [[fig:trema_overview]]。このように、 =trema= -コマンドは Trema フレームワークにおける中心的なツールで、あらゆるコント -ローラ開発の出発点と言えます。 - -#+CAPTION: =trema= コマンドでコントローラを実ネットワークや仮想ネットワークで実行 -#+LABEL: fig:trema_overview -#+ATTR_HTML: :width 600pt -[[./ja/images/openflow_framework_trema/trema_overview.png]] - -=trema= コマンドは =git= や =svn= コマンドと似たコマンド体系を持ってお -り、 =trema= に続けて =run= などのサブコマンドを指定することで様々な機 -能を呼び出します。こうしたコマンド体系を一般に「コマンドスイート」と呼 -びます。 - -一般的なコマンドスイートと同じく、サブコマンドの一覧は =trema help= で -表示できます。また、サブコマンド自体のヘルプは =trema help [サブコマン -ド]= で表示できます。以下に =trema help= で表示されるサブコマンド一覧を -ざっと紹介しておきましょう。いくつかのサブコマンドはまだ使い方を紹介し -ていませんが、続く章で説明しますので今は目を通すだけでかまいません。 - -- =trema run= :: - コントローラをフォアグラウンドで実行する。 - =--daemonize (-d)= オプションを付けるとコントローラをバッ - クグラウンド(デーモンモード)として実行できる -- =trema killall= :: - バックグラウンドで起動している Trema プロセス全体を - 停止する -- =trema version= :: - Tremaのバージョンを表示する。 =trema --version= と - 同じ -- =trema ruby= :: - Trema の Ruby API をブラウザで表示する -- =trema kill [仮想スイッチ]= :: - 仮想ネットワーク内の指定したスイッチを停止する - ([[chap:switch_monitoring_tool]]を参照) -- =trema up [仮想スイッチ]= :: - 仮想ネットワークの指定したスイッチを再起動する - ([[chap:switch_monitoring_tool]]を参照) -- =trema send_packets [送信オプション]= :: - 仮想ネットワーク内でテストパケットを送信する - ([[chap:learning_switch]]を参照) -- =trema show_stats [仮想ホスト名]= :: - 仮想ネットワーク内の仮想ホストで送受信したパケットの統計情報を表示す - る([[chap:learning_switch]]を参照) -- =trema reset_stats= :: - 仮想ネットワーク内の仮想ホストで送受信したパケットの統計情報をリセッ - トする([[chap:learning_switch]]を参照) -- =trema dump_flows [仮想スイッチ名]= :: - 仮想ネットワーク内の仮想スイッチのフローテーブルを表示する - ([[chap:learning_switch]]を参照) - -この章ではさきほど使った =trema run= に加えて、Ruby API を表示する -=trema ruby= コマンドを覚えておいてください。 =trema ruby= を実行すると -デフォルトブラウザで Trema Ruby API リファレンスのページが開きます -([[fig:trema_ruby]])。プログラミング中いつでもコマンド一発でリファレン -スを開けるので大変便利です。 - -#+CAPTION: =trema ruby= コマンドで Trema Ruby API リファレンスを表示したところ -#+LABEL: fig:trema_ruby -#+ATTR_HTML: :width 700pt -[[./ja/images/openflow_framework_trema/trema_ruby.png]] - -では、気になっていた Ruby の文法にそろそろ進みましょう。Part 2 では今後 -もたくさん Ruby を使いますが、その都度必要な文法を説明しますので心配は -いりません。しっかりついてきてください。 - -** 即席 Ruby 入門 - -外国語の習得にも言えることですが、Ruby を習得する一番の近道は登場する品 -詞の種類を押さえておくことです。Ruby に出てくる名前(構成要素)には、その -品詞を見分けるための手がかりとなる視覚的なヒントがかならずあります。名 -前に記号が使われていたり、最初の文字が大文字になっていたりするので、断 -片的なコードを見てもすぐにどんな品詞かわかります。品詞がわかれば、その -Ruby コードがどんな構造かわかります。 - -これからそれぞれの品詞について簡単に説明しますが、最初からすべてが理解 -できなくとも構いません。しばらくすれば Hello, Trema! プログラムのあらゆ -る部分が識別できるようになっているはずです。 - -*** キーワード - -Ruby にはたくさんの組み込みの語があり、それぞれに意味が与えられています。 -これらの語を変数として使ったり、自分の目的に合わせて意味を変えたりはで -きません。 - -#+BEGIN_SRC -alias and BEGIN begin break case class def defined -do else elsif END end ensure false for if -in module next nil not or redo rescue retry -return self super then true undef unless until when -while yield -#+END_SRC - -このうち、「Hello Trema!」では =class= と =def= 、そして =end= キーワー -ドを使いました。 - -#+BEGIN_SRC ruby -n - class HelloTrema < Controller - def start - puts "Hello, Trema!" - end - end -#+END_SRC - -=class= キーワードは続く名前 (=HelloTrema=) のクラスを定義します。この -クラス定義は最後の 5 行目の =end= までです。 =def= キーワードは続く名前 -(=start=) のメソッドを定義します。このメソッド定義は 4 行目の =end= ま -でです。この =def= や =class= で始まって =end= で終わる領域のことをブロッ -クと呼びます。すべての Ruby プログラムはこのブロックがいくつか組み合わ -さったものです。 - -*** 定数 - -=Time= や =Array= や =PORT_NUMBER= など、大文字で始まる名前が定数です。 -定数は Ruby の世界では英語や日本語などの自然言語における固有名詞に当た -ります。 - -英語でも固有名詞は大文字で始めることになっています。たとえば Tokyo -Tower (東京タワー) もそうです。東京タワーは動かすことができませんし、何 -か別なものに勝手に変えることもできません。このように、固有名詞は時間と -ともに変化しないものを指します。そして固有名詞と同様、Ruby の定数は一度 -セットすると変更できません (変更しようとすると警告が出ます)。 - -#+BEGIN_SRC ruby -TokyoTower = "東京都港区芝公園4丁目2-8" -#+END_SRC - -「Hello Trema!」の例では =class= キーワードに続く =HelloTrema= と、 -=Controller= がそれぞれ大文字で始まるので定数です。つまり、クラス名は定 -数なので実行中にその名前を変えることはできません。 - -#+BEGIN_SRC ruby - class HelloTrema < Controller - def start - puts "Hello, Trema!" - end - end -#+END_SRC - -これで「Hello Trema!」の説明に必要な品詞の説明はおしまいです。それでは -「Hello Trema!」の中身を読み解いていきましょう。 - -*** コントローラクラスの定義 - -キーワードの節で説明したように、 =class= キーワードに続く定数から -=end= までで定義されるブロックがクラス定義です。Trema ではすべてのコン -トローラはクラスとして定義され、かならず Trema の =Controller= クラスを -継承します。クラスを継承するには、 - -#+BEGIN_SRC ruby -class クラス名 < 親クラス名 -#+END_SRC - -と書きます。 - -#+BEGIN_SRC ruby - class HelloTrema < Controller - def start - puts "Hello, Trema!" - end - end -#+END_SRC - -=Controller= クラスを継承することで、コントローラに必要な基本機能が -=HelloTrema= クラスにこっそりと追加されます。たとえば雑多な初期化などの -裏仕事を =Controller= クラスが代わりにやってくれるわけです。 - -*** ハンドラメソッドの定義 - -さて、こうして定義した =HelloTrema= はどこから実行が始まるのでしょうか? -C で言う =main()= 関数に当たるものがどこにも見あたりません。 - -その答は Trema の動作モデルであるイベントドリブンモデルにあります。 -Trema のコントローラは、様々な OpenFlow イベントに反応するイベントハン -ドラをまとめたクラスとして定義できます。それぞれのイベントハンドラは、 -対応する OpenFlow イベントが発生したときに自動的に呼び出されます。たと -えば何か OpenFlow メッセージが到着したとき、もしそのメッセージに対応す -るハンドラメソッドがコントローラクラスに定義されていれば、Trema がその -メソッドを発見して呼んでくれます。 - -Trema でよく使われるイベントをここにリストアップします。 - -- =start= :: - コントローラの起動時に呼ばれる -- =switch_ready=, =switch_disconnected= :: - スイッチがコントローラに接続または切断したときに呼ばれる - ([[chap:switch_monitoring_tool]]にて詳説) -- =packet_in= :: - 未知のパケットが到着したという Packet In メッセージ到着 - 時に呼ばれる([[chap:learning_switch]]にて詳説) -- =flow_removed= :: - フローが消された時の Flow Removed メッセージ到着時に - 呼ばれる([[chap:traffic_monitor]]にて詳説) - -ハンドラメソッドの定義は、 =def= キーワードに続く名前から =end= までの -ブロックです。たとえば =HelloTrema= の例では =start= ハンドラメソッドを -定義しており、これがコントローラの起動イベント発生時、つまり =trema -run= でコントローラを起動したときに自動的に呼ばれます。 =start= ハンド -ラメソッド中の =puts= は Ruby 組込みのメソッドで、C の =puts()= 関数と -同じく文字列を標準出力へ改行付きで出力します。 - -#+BEGIN_SRC ruby - class HelloTrema < Controller - def start - puts "Hello, Trema!" - end - end -#+END_SRC - -これで「Hello Trema!」の説明はおしまいです。Trema で作るコントローラは -基本的にこの「Hello, Trema!」と同じ構成をしています。つまり、これをベー -スにいくつか必要なハンドラメソッドを追加していけば、より複雑で実践的な -コントローラも作れます。 - -**** コラム @@html:取間 (とれま) @@先生いわく: ハンドラメソッドの自動呼び出し -:PROPERTIES: -:EXPORT_OPTIONS: num:nil -:END: - -プログラミング経験の長い読者の中には、「ハンドラメソッドを定義しただけ -なのに、なぜ Trema はこのメソッドを自動的にみつけられるんだろう?」と不 -思議に思った人がいるかもしれません。プログラム中にどういう関数があるか -(=コンパイル時情報) をプログラム自身が知る (=実行時) ことはむずかしいか -らです。特に C ではコンパイル時と実行時の間にはぶ厚いカーテンが引かれて -いるので普通は無理です。 - -実は、Ruby にはイントロスペクション(リフレクションや自己反映計算とも呼 -ぶ)と呼ばれる機能があり、オブジェクトが自らの持つメソッドを実行時に調べ -ることができます。たとえば Packet In メッセージが到着したとき、コントロー -ラはイントロスペクションして自分が =packet_in= というメソッドを持ってい -るかどうかを実行時に調べます。そしてもしみつかればそのメソッドを呼んで -あげるというわけです。この機能は =Controller= クラスを継承したときに自 -動的にコントローラへと導入されます。 - -** Tremaのファイル構成 - -最後に Trema のファイル構成を見ておきましょう。Trema をダウンロードする -と、いくつかのファイルとディレクトリがあることがわかります。次に主要な -ものを挙げましょう。 - -- =bin/= :: - 各種コマンドの本体が置かれるディレクトリ -- =build.rb= :: - ビルドスクリプト -- =cruise.rb= :: - すべてのテストコードを実行するテストスイート(Trema 開発 - 者向け) -- =features/= :: - 受け入れテスト一式 (Trema 開発者向け) -- =ruby/= :: - Rubyライブラリのソースコード -- =spec/= :: - Rubyのユニットテスト一式 (Trema 開発者向け) -- =src/examples/= :: - サンプルアプリ -- =src/lib/= :: - Cライブラリのソースコード -- =tmp= :: - ログファイルや PID ファイルといった一時ファイルの置き場 -- =trema= :: - =trema= コマンド -- =unittests/= :: - C のユニットテスト一式 (Trema 開発者向け) - -この中でも Trema でコントローラを作りたい人が読むべきは、サンプルアプリ -(=[trema]/src/examples=)です。 - -*** コラム @@html:取間 (とれま) @@先生いわく: Trema のテスト - -Trema にはずいぶんたくさんのテストコードが付いていて、Trema 開発者がテス -トをとても重視していることがわかると思います。テストの実行頻度も徹底し -ていて、開発者が新しいコードをコミットする度にすべてのテスト -(=cruise.rb= スクリプト)を自動的に実行することで、「いつダウンロードし -ても正しく動く」ことを保証しているのです。この手法をよく「継続的インテ -グレーション」と呼びます。 - -# @warn(テストランプと天井の蛍光灯がかぶって見づらいので、写真を撮り直し) -#+CAPTION: テストの実行結果を示すランプ -#+LABEL: fig:ccrb -#+ATTR_HTML: :width 400pt -[[./ja/images/openflow_framework_trema/ccrb.jpg]] - -Trema を壊さないために、1つおもしろい工夫があります。[[fig:ccrb ]]は Trema -開発者の机に置いてあるランプで、テストの実行結果をランプの色で視覚的に -フィードバックします。テストがすべて通るとランプが緑色に光り、もしエラー -が起こった場合には、ランプが赤く光り開発メンバー全員にメールが飛びます。 -これによって、万が一壊してしまっても必ず誰かが気付けるようにしています。 - -このしくみには、環境構築が手軽な CruiseControl.rb -(http://cruisecontrolrb.thoughtworks.com/) と自作プラグインを使っていま -す。 - -** サンプルアプリ - -サンプルアプリ(=[trema]/src/examples/=)は簡単なOpenFlowアプリケーション -をたくさん含んでおり、実際のAPIの使い方を調べるのに便利です。以下におも -なサンプルアプリをまとめます(括弧内は =[trema]/src/examples/= 内のディ -レクトリ名)。このうちいくつかは続く章で詳しく説明していきます。 - -- こんにちはTrema (=hello_trema=) :: - この章で説明した「Hello Trema!」と表示するだけのサンプル。これを - =trema run= コマンドで実行すれば、手っ取り早く Trema を試すことが - できる (Trema を始めたばかりの初心者向け) -- Packet In (=packet_in=) :: - OpenFlow メッセージの中でも重要な Packet In メッセージをハンドルす - るサンプル。OpenFlow メッセージハンドラの定義方法や、Packet In メッ - セージの取り扱いの基本が学べる -- スイッチの監視 (=switch_monitor=) :: - スイッチがコントローラへ接続したり逆に切断したときのイベントを捕捉 - するサンプル。複数のハンドラを使った少し複雑なコントローラの実装が - 学べる ([[chap:switch_monitoring_tool]]にて詳説) -- OpenFlow メッセージのダンプ (=dumper=) :: - コントローラが受け取るすべての OpenFlow メッセージを文字列としてダ - ンプするサンプル。さまざまなハンドラの書き方リファレンスとして役に - 立つ -- スイッチ情報 (=switch_info=) :: - スイッチの詳細情報を要求する Features Request メッセージをコントロー - ラに送信し、スイッチから受信したスイッチ情報を出力するサンプル。コ - ントローラからスイッチへ OpenFlow メッセージを送る方法が学べる -- リピータハブ (=repeater_hub=) :: - いわゆるバカハブ(ダムハブ)の実装。重要なOpenFlowメッセージである - Flow ModとPacket Outの基本が学べる。[[chap:tdd]]では少し進んだ話題とし - て、これを題材にコントローラのテスト駆動開発手法を学ぶ -- ラーニングスイッチ (=learning_switch=) :: - 普通のスイッチをエミュレートするサンプル。FDB などスイッチの基本構 - 成を学ぶことができる ([[chap:learning_switch]]で詳説) -- トラフィックモニタ (=traffic_monitor=) :: - ラーニングスイッチを拡張し、ユーザごとのトラフィックを測れるように - したもの。フローに含まれる統計情報の利用例として役に立つ - ([[chap:traffic_monitor]]にて詳説) -- 複数スイッチ対応ラーニングスイッチ (=multi_learning_switch=) :: - ラーニングスイッチの複数スイッチ版です。ラーニングスイッチとの違い、 - とくにスイッチごとに FDB を管理する部分に注目してください。 -- シンプルルータ (=simple_router=) :: - ルータの基本機能を実装したサンプル。ルータでのパケットの書き換えと - 転送、およびルーティングテーブルの実装などルータの基本が学べる - ([[chap:router_part1]]および[[chap:router_part2]]で詳説) - -Trema にはたくさんの API があり、上述したサンプルではまだまだすべてを紹 -介しきれていません。新しいサンプルアプリを作った人は、ぜひ GitHub で -pull リクエストを送ってください。あなたの名前が Trema プロジェクトの貢 -献者リスト (https://github.com/trema/trema/graphs/contributors) に載る -かもしれません! - -** まとめ - -さて、これで Trema の基本はおしまいです。この章では Trema をセットアッ -プし、すべてのコントローラのテンプレートとなる「Hello, Trema!」コントロー -ラを書きました。この章で学んだことを簡単にまとめてから、実践的なコント -ローラの開発に入っていくことにしましょう。 - -- Trema は RubyGems またはソースコードからビルドしてインストールできる -- コントローラは =trema run= コマンドでコンパイル無しにすぐ実行できる -- コントローラは Ruby のクラスとして定義し、 =Controller= クラスを継承 - することで必要なメソッドや機能が取り込まれる -- コントローラクラスに各種イベントに対応するハンドラを定義することで - OpenFlow コントローラを実装できる。たとえば、起動イベントに対応するハ - ンドラは =start= -- Trema のファイル構成と主なサンプル一覧 - -これで基礎は十分にできました。次の章では、いよいよ実用的な OpenFlow コ -ントローラを書き実際にスイッチをつないでみます。 - -** 参考文献 - -Ruby プログラミングが初めてという人達のために、入門に役立つサイトや本を -いくつか紹介します。 - -- Why's (Poignant) Guide to Ruby (http://mislav.uniqpath.com/poignant-guide/) :: - 著者は大学や職場でいろいろなプログラミング言語を勉強してきましたが、 - これほど読んでいて楽しい本に出会ったことはありません。この本は - Ruby 界の謎の人物 _why 氏による風変りな Ruby 入門で、プログラミン - グ言語の解説書にもかかわらずまるで小説やマンガのようにリラックスし - て読めます。この章の Ruby の品詞の説明は、この本を参考にしました。 - (日本語版はこちら - http://www.aoky.net/articles/why_poignant_guide_to_ruby/)。 -- TryRuby (http://tryruby.org/) :: - 同じく _why 氏によるブラウザで動く Ruby 環境です。Ruby を試してみ - たいけどインストールするのが面倒という人は、まずはここで Ruby を試 - してみましょう。 =help= と打つと 15 分の短い Ruby チュートリアルが - 始まります。 -- プログラミング Ruby 第 2 版 (Dave Thomas、Chad Fowler、Andrew Hunt 著/田和勝、まつもとゆきひろ 訳/オーム社) :: - Ruby の完全なリファレンスです。本気で Ruby を勉強したい人は持って - いて損はしません。この本だけあれば十分です。 diff --git a/tasks/deploy.rake b/tasks/deploy.rake new file mode 100644 index 00000000..31ca809f --- /dev/null +++ b/tasks/deploy.rake @@ -0,0 +1,16 @@ +# rubocop:disable LineLength +task :deploy do + if ENV['TRAVIS_BRANCH'] != 'master' + fail 'This is not a master branch. No deployment will be done.' + end + if ENV['TRAVIS_PULL_REQUEST'] != 'false' + fail 'This is a pull request. No deployment will be done.' + end + sh 'rm .gitignore' + sh 'git checkout -B gh-pages' + sh 'bundle exec rake html' + sh 'git add -A .' + sh %(git commit --quiet -m "Travis build #{ENV['TRAVIS_BUILD_NUMBER']}") + sh %(git push --force --quiet "https://#{ENV['GH_TOKEN']}@github.com/yasuhito/trema-book.git" gh-pages > /dev/null), verbose: false +end +# rubocop:enable LineLength diff --git a/tasks/html.rake b/tasks/html.rake new file mode 100644 index 00000000..bc51c448 --- /dev/null +++ b/tasks/html.rake @@ -0,0 +1,11 @@ +require 'rake/clean' + +CLOBBER << 'index.html' + +task html: 'index.html' + +# rubocop:disable LineLength +file 'index.html' => ['index.adoc', 'hello_trema.adoc'] do |t| + sh "bundle exec asciidoctor -a icons=font -a toc=left -a source-highlighter=coderay -d book index.adoc --out-file #{t.name}" +end +# rubocop:enable LineLength diff --git a/tasks/pdf.rake b/tasks/pdf.rake new file mode 100644 index 00000000..2aa85cfc --- /dev/null +++ b/tasks/pdf.rake @@ -0,0 +1,19 @@ +require 'rake/clean' + +CLEAN << 'book.xml' +CLOBBER << 'book.pdf' + +task pdf: 'book.pdf' + +file 'book.pdf' => 'book.xml' do + fopub_options = %w(-param body.font.family VL-PGothic-Regular + -param dingbat.font.family VL-PGothic-Regular + -param monospace.font.family VL-PGothic-Regular + -param sans.font.family VL-PGothic-Regular + -param title.font.family VL-PGothic-Regular) + sh "./vendor/asciidoctor-fopub/fopub book.xml #{fopub_options.join(' ')}" +end + +file 'book.xml' => ['book.adoc', 'hello_trema.adoc'] do + sh 'bundle exec asciidoctor -b docbook -d book -a data-uri! book.adoc' +end diff --git a/tasks/rubocop.rake b/tasks/rubocop.rake new file mode 100644 index 00000000..ddbc893d --- /dev/null +++ b/tasks/rubocop.rake @@ -0,0 +1,8 @@ +begin + require 'rubocop/rake_task' + RuboCop::RakeTask.new +rescue LoadError + task :rubocop do + $stderr.puts 'RuboCop is disabled' + end +end diff --git a/vendor/asciidoctor-fopub/.gitignore b/vendor/asciidoctor-fopub/.gitignore new file mode 100644 index 00000000..abc78ba7 --- /dev/null +++ b/vendor/asciidoctor-fopub/.gitignore @@ -0,0 +1,2 @@ +/.gradle/ +/build/ diff --git a/vendor/asciidoctor-fopub/LICENSE b/vendor/asciidoctor-fopub/LICENSE new file mode 100644 index 00000000..0e4fd17e --- /dev/null +++ b/vendor/asciidoctor-fopub/LICENSE @@ -0,0 +1,21 @@ +The MIT License + +Copyright (C) 2013 Dan Allen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/asciidoctor-fopub/README.adoc b/vendor/asciidoctor-fopub/README.adoc new file mode 100644 index 00000000..08f9616c --- /dev/null +++ b/vendor/asciidoctor-fopub/README.adoc @@ -0,0 +1,361 @@ += asciidoctor-fopub +Dan Allen +v0.2.0, 2013-11-27 +ifdef::basebackend-docbook[:doctype: book] +:license: https://github.com/asciidoctor/asciidoctor-fopub/blob/master/LICENSE[MIT] +:idprefix: +:idseparator: - +:experimental: + +DocBook-to-PDF conversion using free software made easy! (based on DocBook XSL and Apache FOP) + +Using the asciidoctor-fopub project, you can convert any DocBook file into a nicely formatted PDF with nothing more than a Java runtime (JVM) and development kit (JDK). +All the open source software required to perform the conversion is automatically fetched from the internet the first time you run it. + +[NOTE,caption=Scope] +fopub is a replacement for `a2x` command from AsciiDoc Python. +a2x is a standalone frontend to the DocBook toolchain. +fopub (like a2x) is meant to be used when you don't have a Gradle (or Maven) build. +Once you are in the Gradle world, you can simply leverage the jDocBook plugin to accomplish what fopub is doing (as seen in the Spring and Hibernate builds). + +== Doing is believing + +Before we get into the goals and the technical details of the project, let's see the conversion in action! + +=== Prerequisites + +The only prerequisite to perform the DocBook to PDF conversion is a Java Development Kit (JDK), which includes the Java runtime (and, naturally, an internet connection). +You can use any recent JDK (i.e., Java SE 6 or better). + +If Java runtime (JVM) is setup correctly, you should be able to type: + + $ java -version + +or + + $ "$JAVA_HOME/bin/java -version" + +and see output that looks like: + +.... +java version "1.7.0_25" +OpenJDK Runtime Environment (fedora-2.3.10.3.fc17-x86_64) +OpenJDK 64-Bit Server VM (build 23.7-b01, mixed mode) +.... + +If the Java Devlopment Kit (JDK) is setup correctly, you should be able to type: + + $ javac -version + +or + + $ "$JAVA_HOME"/bin/javac -version" + +and see output that looks like: + +.... +javac 1.7.0_25 +.... + +If not, you can install a JDK using your system's package manager or by downloading a distribution from the http://www.oracle.com/technetwork/java/javase/downloads/index.html[Java for developers download site]. + +WARNING: You may encounter problems if you attempt to use the GNU compiler for Java (gcj). +We recommend using OpenJDK or Oracle Java. + +TIP: It's not even necessary to install the JDK on your system. +Simply set the `JAVA_HOME` environment variable to the location where you extract the distribution. + +Next, you need to retrieve the asciidoctor-fopub project. + +=== Retrieve the project + +You can retrieve the asciidoctor-fopub project in one of two ways: + +. Clone the git repository +. Download a zip archive of the repository + +==== Option 1: Fetch using git clone + +If you want to clone the git repository, simply copy the URL of the repository on GitHub and pass it to `git clone` command: + + $ git clone https://github.com/asciidoctor/asciidoctor-fopub + +Next, change to the project directory: + + $ cd asciidoctor-fopub + +==== Option 2: Download the archive + +If you want to download a zip archive, click on the btn:[Download Zip] button on the right-hand side of the repository page on GitHub. +Once the download finishes, extract the archive, open a console and change to that directory. + +TIP: Instead of working out of the asciidoctor-fopub directory, you can simply add the directory to your `PATH` environment variable. + +Next, let's grab a file to convert. + +=== Generate a DocBook file + +If you don't already have a DocBook file, you can generate one using http://asciidoctor.org[Asciidoctor] (or your tool of choice). + +To create a DocBook file using Asciidoctor, first create an AsciiDoc file named [file]_sample.adoc_ in the current directory and populate it the sample content below. + +.sample.adoc +[source,asciidoc] +.... += Document Title +Doc Writer + +A sample http://asciidoc.org[AsciiDoc] document. + +== Introduction + +A paragraph followed by a simple list. + +* item 1 +* item 2 + +Here's how you say "`Hello, World!`" in Ruby. + +.A basic Ruby application +[source,ruby] +---- +puts "Hello, World!" +---- + +TIP: asciidoctor-fopub takes the pain out of converting a DocBook file to a PDF file like this one. +All you need is a Java Development Kit (JDK) and this project. +The rest of the software is fetched and configured by Gradle. +.... + +Convert the AsciiDoc file to DocBook using the `asciidoctor` (or `asciidoc`) command: + + $ asciidoctor -b docbook -d book -a data-uri! sample.adoc + +After executing this command, you should now see a new file named [file]_sample.xml_ in the current directory. + +IMPORTANT: We explicitly disable the `data-uri` attribute just in case it's set in the AsciiDoc document. +The PDF processor will choke if it comes across embedded image data in the generated DocBook. + +It's time to convert it to PDF! + +TIP: You could also try these steps using the [file]_README.adoc_ file in the root directory of the project. + +=== Convert DocBook to PDF + +We're now ready to do the conversion! +It's as simple as running the `fopub` script in the current directory on our DocBook file. + +On Unix-based systems (e.g., Linux, OSX), run: + + $ ./fopub sample.xml + +IMPORTANT: Since we're executing a local script, you need to prefix the name of the command with `./`. + +TIP: If you've added the path to asciidoctor-fopub to your `PATH` environment variable, you can leave off the `./` and execute it from any directory. + +On Windows, run: + + $ fopub sample.xml + +NOTE: The first time you run the command, asciidoctor-fopub has to retrieve the software from the repositories and setup the conversion application, so be patient. + +TIP: To use the `fopub` command from behind a proxy, you'll need to configure the Gradle proxy settings as described in http://www.gradle.org/docs/current/userguide/build_environment.html[Chapter 20 of the Gradle Reference Guide]. + +When it's all said and done, you should now see the file [file]_sample.pdf_ in the current directory. +Open that file with a PDF viewer to see the result. + +.Sample PDF document rendered in viewer +image::sample-pdf-screenshot.png[Screenshot of sample PDF document] + +As you can see, all the details of the conversion are hidden behind the scenes. +You get to focus on getting the job done, not worry about the mess that has to be sorted out to use Apache FOP correctly and get a decent-looking document. + +=== Custom XSL parameters + +Any arguments that follow the source file name are passed directly to the Apache FO processor (fop). +This feature allows you to assign XSL parameters, among other things. + +Let's say you want to set the orientation of the PDF to landscape. +The DocBook XSL templates recognize the parameter named `page.orientation`. +Here's how you would pass that through fopub to fop. + + $ ./fopub sample.xml -param page.orientation landscape + +You'll now notice that the PDF generated is rendered in landscape mode. + +NOTE: See http://docbook.sourceforge.net/release/xsl/1.78.1/doc/param.html[DocBook XSL parameter reference] for a list of all XSL parameters you can set. + +CAUTION: Custom parameters are currently only implemented in the Unix version of the fopub script. + +=== Custom XSL templates + +When you work on many documentations projets in *parallel*, you will probably need different outputs. + +.Use cases for different templates + +structure:: + + . One needs a picture in the book title page + . The other needs a special text at the bottom of this page + +style:: + + . One needs the default Asciidoctor style + . The other uses the Colony style + +In order to work on different documentation projects _in parallel_, you need to have different docbook-xsl directories. + +How it works: + +. Copy the `docbook-xsl` directory from the fopub install directory to your documentation path, [file]_/path/to/custom/fopub_. ++ +.Content of the docbook-xsl directory +image::docbook-xsl-content.png[Content of docbook-xsl directory] + +. Update the files you want in this directory ++ +For example if you want to use Colony style:: + You delete Asciidoctor theme, Foundation theme and you uncomment Colony theme in the `common.xsl` file. + +. Then you specify this directory when you launch the output generation : + + $ /path/to/fopub/fopub -t /path/to/custom/fopub/docbook-xsl sample.xml + +=== fopub option flags + +-t :: + tells fopub which docbook-xsl directory to use (optional, defaults to location inside fopub installation) +-f [pdf|ps|fo]:: + tells fopub which output to produce (optional, defaults to `pdf`) +-h:: + prints usage + +== Motivation + +The asciidoctor-fopub project aims to provide a simple mechanism for converting DocBook to PDF. +The plan is to use some form of this project to handle the DocBook to PDF conversion in http://asciidoctor.org[Asciidoctor]. +We hope it's generally useful outside of Asciidoctor as well. + +If you've ever had to do this conversion, you will appreciate how overly-complex it is. +It requires fetching the right combination of software (including the right versions), putting all the files in the right location and associating them together using a catalog and passing in the correct parameters. +_It's boring and tedious._ +This project handles all that magic so you don't have to. + +In addition to making the conversion work, the project includes the following features that are often left out: + +* Works with DocBook generated by AsciiDoc (supports all AsciiDoc processing instructions) +* Syntax highlights source code listings using http://sourceforge.net/projects/xslthl[XSLTHL] (including a highlighter for AsciiDoc source) +* Scales down images to fit within the width of the page +* Applies (configurable) formatting and styling that's consistent with the Asciidoctor themes +* Loads and embeds system fonts necessary to support the themes (Arial, Georgia and Liberation Mono) +* Applies configuration to embed SVG-based admonition icons and callout marks +* Works without an internet connection (once the initial run is complete); _drastically speeds up execution_ +* Works from any directory (planned) + +There's a lot of research that went into making all that happen for you :) + +NOTE: One of the most important features of this tool--and one of the most difficult to get right--is that it works offline. +By default, XSL processors fetch all necessary resources from the internet. +Since these files are large and reference many other files, fetching them from the internet is exceptionally slow and a waste of network bandwidth. +The `fopub` tool carefully ensures that the processor has all the files it needs (on the first run) and thus keeps it from reaching out to the internet while it performs the conversion. + +== Technical details + +Let's talk tech. + +=== The conversion's key players: Apache FOP and DocBook XSL + +The main goal of this project is to download, configure and execute http://xmlgraphics.apache.org/fop[Apache FOP] to handle the conversion from DocBook to PDF using the http://en.wikipedia.org/wiki/DocBook_XSL[DocBook XSL] stylesheets. +You can see from the first part of the http://www.sagehill.net/docbookxsl[DocBook XSL book] what a complex proposition this is. + +DocBook XSL:: The purpose of DocBook XSL is to provide a standard set of XML transformations (XSLT) from DocBook to several presentational formats, one of which is XSL-FO. + +Apache FOP:: Apache FOP (Formatting Objects Processor) is a print formatter driven by XSL formatting objects (XSL-FO) and an output independent formatter. +It is a Java application that reads a formatting object (FO) tree and renders the resulting pages to a specified output. +The primary output target is PDF. + +Apache FOP also includes an XSLT processor (Xalan) that handles the conversion from DocBook into the intermediatory XSL-FO format that the print formatter uses to create the PDF. + +When the software is all setup, we are ultimately running a command in the `fopub` script similar to this one: + + $ fop -xml sample.xml -xsl docbook-xsl/fo-pdf.xsl -pdf sample.pdf + +In reality, it's more complex. +You can see the full command at the bottom of the `fopub` script. + +=== Source highlighting using XSLTHL + +Readers have come to expect source code to be highlighted so it looks the same way in the document as it does in their source code editors. +http://sourceforge.net/projects/xslthl[XSLTHL] provides source highlighting for PDF output. + +XSLTHL integration is a well-hidden feature in the DocBook XSL project. +It requires a Java-based XSLT processor (like the one embedded in Apache FOP) to use it. +Activating it requires passing special parameters to the processor that specify the location of the configuration file and a flag to turn it on. + +When everything falls into place, as it does with the `fopub` script, you get nice syntax highlighting in your PDF file and happy readers. + +=== Priming the pump with Gradle + +Setting up a Java application is no small feat. +So what fetches the software and puts it all in the right place? +That handywork is performed by Gradle. + +http://www.gradle.org[Gradle] is a Java-based automation and build tool that specializes in setting up Java applications (among other capabilities). +Gradle can fetch files from remote repositories, move them around, create start scripts and assemble an application distribution. + +We are using Gradle to prepare a Java application into the `build/fopub` directory that the `fopub` script can execute. + +You may be wondering why Gradle isn't a prerequisite of this project. +The answer is that the Gradle project provides a tool that can bootstrap Gradle from nothing. +That tool, `gradlew`, is included with the project. +It gets invoked the first time you run the `fopub` script. +*Magic.* + +And that's essentially what this project is all about, *magic*. +Converting from DocBook to PDF shouldn't be hard. +We are doing our best to hide those details so that it's as simple as it should be. + +== Software versions + +[cols="2*", options="header"] +|=== +|Software Project |Version + +|Apache FOP +|1.1 + +|DocBook XSL +|1.78.1 + +|Apache Commons XML Resolver +|1.2 + +|Xalan +|2.6.0 + +|XSLTHL +|2.1.0 + +|Gradle +|2.0 +|=== + +== Contributing + +In the spirit of free software, _everyone_ is encouraged to help improve this project. + +To contribute code, simply fork the project on GitHub, hack away and send a pull request with your proposed changes. + +Feel free to use the https://github.com/asciidoctor/asciidoctor-fopub/issues[issue tracker] or http://discuss.asciidoctor.org[Asciidoctor mailing list] to provide feedback or suggestions in other ways. + +== Authors + +*asciidoctor-fopub* was written by https://github.com/mojavelinux[Dan Allen]. +It builds on prior work done by authors of the http://asciidoc.org[AsciiDoc], https://github.com/pressgang/jdocbook-core[jDocBook] and http://www.jboss.org/pressgang[PressGang] projects. + +== Copyright + +Copyright (C) 2013 Dan Allen. +Free use of this software is granted under the terms of the MIT License. + +See the link:LICENSE[LICENSE] file for details. diff --git a/vendor/asciidoctor-fopub/asciidoctor-fopub.gemspec b/vendor/asciidoctor-fopub/asciidoctor-fopub.gemspec new file mode 100644 index 00000000..a65bd66f --- /dev/null +++ b/vendor/asciidoctor-fopub/asciidoctor-fopub.gemspec @@ -0,0 +1,25 @@ +# coding: utf-8 +lib = File.expand_path('../lib', __FILE__) +$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) +require 'asciidoctor/fopub/version' + +Gem::Specification.new do |spec| + spec.name = "asciidoctor-fopub" + spec.version = Asciidoctor::Fopub::VERSION + spec.authors = ["Dan Allen"] + spec.email = ['dan.j.allen@gmail.com'] + spec.summary = %q{A portable DocBook-to-PDF build command that wraps DocBook XSL and Apache FOP} + spec.description = %q{DocBook-to-PDF conversion using free software made easy! (based on DocBook XSL and Apache FOP) + +Using the asciidoctor-fopub project, you can convert any DocBook file into a nicely formatted PDF with nothing more than a Java runtime (JVM) and development kit (JDK). All the open source software required to perform the conversion is automatically fetched from the internet the first time you run it.} + spec.homepage = "https://github.com/asciidoctor/asciidoctor-fopub" + spec.license = "MIT" + + spec.files = `git ls-files -z`.split("\x0") + spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } + spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) + spec.require_paths = ["lib"] + + spec.add_development_dependency "bundler", "~> 1.7" + spec.add_development_dependency "rake", "~> 10.0" +end diff --git a/vendor/asciidoctor-fopub/bin/fopub b/vendor/asciidoctor-fopub/bin/fopub new file mode 100755 index 00000000..1e8695ad --- /dev/null +++ b/vendor/asciidoctor-fopub/bin/fopub @@ -0,0 +1,11 @@ +#!/usr/bin/env ruby +parent_dir = File.dirname(File.absolute_path(File.dirname(__FILE__))) +if /cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM + # TODO: test this code on Windows + command = 'cmd.exe' + args = ['/c', File.join(parent_dir, 'fopub.bat')].concat(ARGV) +else + command = File.join(parent_dir, 'fopub') + args = ARGV +end +exec command, *args diff --git a/vendor/asciidoctor-fopub/build.gradle b/vendor/asciidoctor-fopub/build.gradle new file mode 100644 index 00000000..ca56470c --- /dev/null +++ b/vendor/asciidoctor-fopub/build.gradle @@ -0,0 +1,101 @@ +apply plugin: 'application' + +group = 'org.asciidoctor' +version = '0.1.4-SNAPSHOT' + +repositories { + mavenCentral() + mavenLocal() // snags valid artifacts from local Maven repository +} + +configurations { + dist +} + +dependencies { + // xml-resolver needed to fix buggy resolver in JDK + runtime 'xml-resolver:xml-resolver:1.2' + // avalon dependencies need to be specified to align with version available + compile 'org.apache.avalon.framework:avalon-framework-api:4.3.1' + compile 'org.apache.avalon.framework:avalon-framework-impl:4.3.1' + compile 'org.apache.xmlgraphics:fop:1.1' + runtime 'net.sf.xslthl:xslthl:2.1.0' + //dist 'net.sf.docbook:docbook-xsl:1.78.0:ns-resources@zip' +} + +mainClassName = 'org.apache.fop.cli.Main' +applicationDefaultJvmArgs = ["-Dxml.catalog.files=APP_DIR/catalog.xml"] +//applicationDefaultJvmArgs = ['-Dxml.catalog.files=/etc/xml/catalog'] + +installApp { + destinationDir = file("$buildDir/${project.name}") +} + +task downloadDocbookXsl(type: Download) { + url = 'http://downloads.sourceforge.net/project/docbook/docbook-xsl-ns/1.78.1/docbook-xsl-ns-1.78.1.zip' + destinationFile = file("$buildDir/docbook-xsl-ns-1.78.1.zip") +} + +task extractDocbookXsl(type: Copy, dependsOn: downloadDocbookXsl) { + // if ony 1.78.1 were available in the Maven repository :( + //def archive = null + //configurations.dist.each { File file -> + // if (file.name.endsWith('.zip')) { + // archive = file + // return + // } + //} + def archive = file("$buildDir/docbook-xsl-ns-1.78.1.zip") + + def outputDir = file("$buildDir/unpacked/docbook") + // extract zip, stripping root directory + from(zipTree(archive)) { + eachFile { details -> + details.path = details.path.substring(details.relativePath.segments[0].length()) + } + } + into outputDir +} + +task downloadDocbookXml(type: Download) { + url = 'http://maven-us.nuxeo.org/nexus/content/repositories/public/docbook/docbook-xml/4.5/docbook-xml-4.5.jar' + destinationFile = file("$buildDir/docbook-xml-4.5.jar") +} + +task extractDocbookXml(type: Copy, dependsOn: downloadDocbookXml) { + def archive = file("$buildDir/docbook-xml-4.5.jar") + def outputDir = file("$buildDir/unpacked/docbook-dtds") + from(zipTree(archive)) + into outputDir +} + +// put the official DocBook resources (XSL, images, etc) into the dist +applicationDistribution.from(extractDocbookXsl) { + into "docbook" +} + +// put the DocBook DTDs into the dist +applicationDistribution.from(extractDocbookXml) { + into "docbook/xml-dtd-4.5" +} + +class Download extends DefaultTask { + @Input + String url + + @OutputFile + File destinationFile + + @TaskAction + def downloadFile() { + destinationFile.bytes = new URL(url).bytes + } +} + +CreateStartScripts startScripts = project.startScripts +startScripts.with { + doLast { + unixScript.text = unixScript.text.replaceFirst('APP_DIR', '\\$APP_DIR') + windowsScript.text = windowsScript.text.replaceFirst('APP_DIR', '%APP_HOME%') + } +} diff --git a/vendor/asciidoctor-fopub/docbook-xsl-content.png b/vendor/asciidoctor-fopub/docbook-xsl-content.png new file mode 100644 index 00000000..2a6dba33 Binary files /dev/null and b/vendor/asciidoctor-fopub/docbook-xsl-content.png differ diff --git a/vendor/asciidoctor-fopub/fopub b/vendor/asciidoctor-fopub/fopub new file mode 100755 index 00000000..4a02d64e --- /dev/null +++ b/vendor/asciidoctor-fopub/fopub @@ -0,0 +1,138 @@ +#!/bin/bash + +abspath() { + PATH_REL_DIRNAME=`dirname "$1"` + PATH_ABS_DIRNAME=`cd "$PATH_REL_DIRNAME"; pwd` + PATH_BASENAME=`basename "$1"` + if [ "$PATH_BASENAME" == "." ]; then + echo "$PATH_ABS_DIRNAME" + else + echo "$PATH_ABS_DIRNAME/$PATH_BASENAME" + fi +} + +usage() { + echo "Usage: $0 [-t ] [-f FORMAT] FILE" + echo "Example: $0 -t /path/to/custom/docbook-xsl mydoc.xml" +} + +while getopts ':f:ht:' opt; do + case $opt in + f) + FORMAT=$OPTARG + ;; + h) + usage + exit 0 + ;; + t) + DOCBOOK_XSL_DIR=$OPTARG + ;; + \?) + echo "$0: unrecognized option '$1'" + usage + exit 1 + esac +done + +shift $(( $OPTIND -1 )) + +if [ ! -r "$1" ]; then + echo "$0: You must specify a DocBook v4.5 or DocBook v5 XML source file as the first command argument" + usage + exit 1 +fi + +SOURCE_FILE="$1" +SOURCE_EXTENSION="${SOURCE_FILE##*.}" +shift + +if [ "$SOURCE_EXTENSION" != "xml" ] && [ "$SOURCE_EXTENSION" != "fo" ]; then + echo $0: Invalid source file name: "$SOURCE_FILE" + echo "$0: The source file name must end in .fo (XSL-FO) or .xml (DocBook)" + usage + echo "Try \`asciidoctor -b docbook $1\` to convert your AsciiDoc source file to DocBook" + exit 1 +fi + +if [ -z "$FORMAT" ]; then + FORMAT=pdf +fi + +PRG_DIR=`dirname "$0"` +PRG_ABS_DIR=`abspath "$PRG_DIR"` +GRADLEW_CMD="$PRG_DIR/gradlew" +FOPUB_DIR="$PRG_DIR/build/fopub" +FOPUB_ABS_DIR="$PRG_ABS_DIR/build/fopub" +FOPUB_CMD="$FOPUB_DIR/bin/fopub" +# APP_DIR allows this script to invoke a distributed version of the application +if [ -z "$APP_DIR" ]; then + export APP_DIR="$FOPUB_DIR" +fi + +if [ ! -e "$FOPUB_CMD" ]; then + echo Initializing application... + "$GRADLEW_CMD" -p "$PRG_DIR" -q -u installApp + if [ $? != 0 ]; then + echo Failed to initialize application + exit 1 + fi + echo Application initialized! +fi + +SOURCE_ROOTNAME="${SOURCE_FILE%.*}" +OUT_FILE="$SOURCE_ROOTNAME.$FORMAT" + +DOCBOOK_DIR="$FOPUB_DIR/docbook" + +if [ ! -z "$DOCBOOK_XSL_DIR" ]; then + DOCBOOK_XSL_ABS_DIR=`readlink -f "$DOCBOOK_XSL_DIR" 2>/dev/null` + if [ $? -ne 0 ]; then + DOCBOOK_XSL_ABS_DIR=`greadlink -f "$DOCBOOK_XSL_DIR" 2>/dev/null` + if [ $? -ne 0 ]; then + DOCBOOK_XSL_ABS_DIR="$DOCBOOK_XSL_DIR" + fi + fi +else + DOCBOOK_XSL_DIR="$FOPUB_DIR/docbook-xsl" + DOCBOOK_XSL_ABS_DIR="$FOPUB_ABS_DIR/docbook-xsl" +fi + +XSLTHL_CONFIG_URI="file://$DOCBOOK_XSL_ABS_DIR/xslthl-config.xml" + +CONVERT_ARGS="-$SOURCE_EXTENSION \"$SOURCE_FILE\"" + +if [ "$SOURCE_EXTENSION" == "xml" ]; then + CONVERT_ARGS="$CONVERT_ARGS -xsl \"$DOCBOOK_XSL_DIR/fo-pdf.xsl\"" + if [ `grep -c '^ %db5ent;]>\n<\1:" "$SOURCE_FILE" + fi +fi + +case $FORMAT in + pdf|ps) + CONVERT_ARGS="$CONVERT_ARGS -$FORMAT \"$OUT_FILE\"" + ;; + fo) + CONVERT_ARGS="$CONVERT_ARGS -foout \"$OUT_FILE\"" + ;; + *) + echo Unexpected output format: "$FORMAT" + echo Valid options: pdf, ps, fo + usage + exit 1 + ;; +esac + +eval $FOPUB_CMD -q -catalog \ + -c \"$DOCBOOK_XSL_DIR/fop-config.xml\" \ + $CONVERT_ARGS \ + -param highlight.xslthl.config \"$XSLTHL_CONFIG_URI\" \ + -param admon.graphics.path \"$DOCBOOK_DIR/images/\" \ + -param callout.graphics.path \"$DOCBOOK_DIR/images/callouts/\" "$@" + +# NOTE use ignore.image.scaling to disable the image scaling logic in fo-pdf.xsl +# -param ignore.image.scaling \"1\" \ + +exit 0 diff --git a/vendor/asciidoctor-fopub/fopub.bat b/vendor/asciidoctor-fopub/fopub.bat new file mode 100644 index 00000000..e74bc258 --- /dev/null +++ b/vendor/asciidoctor-fopub/fopub.bat @@ -0,0 +1,81 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem fopub script for Windows +@rem +@rem WARNING: This script has not yet been tested! +@rem +@rem ########################################################################## + +if "%OS%"=="Windows_NT" setlocal + +@rem Store full-qualified drive + path of this script +set PRG_DIR=%~dps0 +if "%PRG_DIR%" == "" set PRG_DIR=. + +set GRADLEW_CMD=%PRG_DIR%gradlew +set FOPUB_DIR=%PRG_DIR%\build\fopub +set FOPUB_CMD=%FOPUB_DIR%\bin\fopub.bat + +set DOCBOOK_DIR=%FOPUB_DIR%\docbook +set DOCBOOK_XSL_DIR=%FOPUB_DIR%\docbook-xsl +set XSLTHL_CONFIG_URI=file:///%DOCBOOK_XSL_DIR%\xslthl-config.xml + +:init +set SOURCE_FILE= +set TYPE=pdf +@rem Process first argument +if %1a==a goto endInit +@rem Store fully-qualified drive+path+name+extension of first argument +set SOURCE_FILE=%~f1 +@rem Store fully-qualified drive+path+name of first argument +set SOURCE_ROOTNAME=%~dpn1 +shift +@rem Process second argument +if %1a==a goto endInit +set TYPE=%1 +shift +:endInit + +if "%SOURCE_FILE%" == "" ( + echo . + echo You must specify a DocBook XML source file as the first command argument + echo . + goto fail +) + +:install +if exist "%FOPUB_CMD%" goto endInstall +echo . +echo Initializing application... +"%GRADLEW_CMD%" -q -u installApp +if not "%ERRORLEVEL%"=="0" goto fail +echo Application initialized! +echo . +:endInstall + +SETLOCAL ENABLEDELAYEDEXPANSION + +@rem Add file protocol +set DOCBOOK_DIR_PARAM=file:///%DOCBOOK_DIR% +@rem replacing \ with / +set DOCBOOK_DIR_PARAM=!DOCBOOK_DIR_PARAM:\=/! +set XSLTHL_CONFIG_URI=!XSLTHL_CONFIG_URI:\=/! + +if "%TYPE%" == "pdf" ( + set OUTPUT_PDF_FILE="%SOURCE_ROOTNAME%.pdf" + %FOPUB_CMD% -q -catalog -c "%DOCBOOK_XSL_DIR%\fop-config.xml" -xml "%SOURCE_FILE%" -xsl "%DOCBOOK_XSL_DIR%\fo-pdf.xsl" -pdf !OUTPUT_PDF_FILE! -param highlight.xslthl.config "%XSLTHL_CONFIG_URI%" -param admon.graphics.path "%DOCBOOK_DIR_PARAM%/images/" -param callout.graphics.path "%DOCBOOK_DIR_PARAM%/images/callouts/" + if not "%ERRORLEVEL%"=="0" goto fail else goto mainEnd +) + +if "%TYPE%" == "fo" ( + set OUTPUT_FO_FILE="%SOURCE_ROOTNAME%.fo" + %FOPUB_CMD% -q -catalog -c "%DOCBOOK_XSL_DIR%\fop-config.xml" -xml "%SOURCE_FILE%" -xsl "%DOCBOOK_XSL_DIR%\fo-pdf.xsl" -foout !OUTPUT_FO_FILE! -param highlight.xslthl.config "%XSLTHL_CONFIG_URI%" -param admon.graphics.path "%DOCBOOK_DIR_PARAM%/images/" -param callout.graphics.path "%DOCBOOK_DIR_PARAM%/images/callouts/" + if not "%ERRORLEVEL%"=="0" goto fail else goto mainEnd +) + +:fail +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal diff --git a/vendor/asciidoctor-fopub/gradle/wrapper/gradle-wrapper.jar b/vendor/asciidoctor-fopub/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..b979729d Binary files /dev/null and b/vendor/asciidoctor-fopub/gradle/wrapper/gradle-wrapper.jar differ diff --git a/vendor/asciidoctor-fopub/gradle/wrapper/gradle-wrapper.properties b/vendor/asciidoctor-fopub/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..3fb9f927 --- /dev/null +++ b/vendor/asciidoctor-fopub/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Wed Aug 07 18:28:56 MDT 2013 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=http\://services.gradle.org/distributions/gradle-2.0-bin.zip diff --git a/vendor/asciidoctor-fopub/gradlew b/vendor/asciidoctor-fopub/gradlew new file mode 100755 index 00000000..91a7e269 --- /dev/null +++ b/vendor/asciidoctor-fopub/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/vendor/asciidoctor-fopub/gradlew.bat b/vendor/asciidoctor-fopub/gradlew.bat new file mode 100644 index 00000000..aec99730 --- /dev/null +++ b/vendor/asciidoctor-fopub/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/vendor/asciidoctor-fopub/lib/asciidoctor/fopub.rb b/vendor/asciidoctor-fopub/lib/asciidoctor/fopub.rb new file mode 100644 index 00000000..8091e060 --- /dev/null +++ b/vendor/asciidoctor-fopub/lib/asciidoctor/fopub.rb @@ -0,0 +1,7 @@ +require "asciidoctor/fopub/version" + +module Asciidoctor + module Fopub + # Your code goes here... + end +end diff --git a/vendor/asciidoctor-fopub/lib/asciidoctor/fopub/version.rb b/vendor/asciidoctor-fopub/lib/asciidoctor/fopub/version.rb new file mode 100644 index 00000000..9f65c840 --- /dev/null +++ b/vendor/asciidoctor-fopub/lib/asciidoctor/fopub/version.rb @@ -0,0 +1,5 @@ +module Asciidoctor + module Fopub + VERSION = "0.0.1" + end +end diff --git a/vendor/asciidoctor-fopub/sample-pdf-screenshot.png b/vendor/asciidoctor-fopub/sample-pdf-screenshot.png new file mode 100644 index 00000000..5bd9dfd9 Binary files /dev/null and b/vendor/asciidoctor-fopub/sample-pdf-screenshot.png differ diff --git a/vendor/asciidoctor-fopub/settings.gradle b/vendor/asciidoctor-fopub/settings.gradle new file mode 100644 index 00000000..96a30b0e --- /dev/null +++ b/vendor/asciidoctor-fopub/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'fopub' diff --git a/vendor/asciidoctor-fopub/src/dist/catalog.xml b/vendor/asciidoctor-fopub/src/dist/catalog.xml new file mode 100644 index 00000000..c6c3135d --- /dev/null +++ b/vendor/asciidoctor-fopub/src/dist/catalog.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/asciidoctor-fopub/src/dist/db5.ent b/vendor/asciidoctor-fopub/src/dist/db5.ent new file mode 100644 index 00000000..f380e19c --- /dev/null +++ b/vendor/asciidoctor-fopub/src/dist/db5.ent @@ -0,0 +1,5 @@ + + + + +%dbcent; diff --git a/vendor/asciidoctor-fopub/src/dist/docbook-xsl/callouts.xsl b/vendor/asciidoctor-fopub/src/dist/docbook-xsl/callouts.xsl new file mode 100644 index 00000000..e4abaeb5 --- /dev/null +++ b/vendor/asciidoctor-fopub/src/dist/docbook-xsl/callouts.xsl @@ -0,0 +1,29 @@ + + + + + + 1 + 60 + 9pt + 1 + 10 + .svg + + + + + + + + images/icons/callouts/ + + + diff --git a/vendor/asciidoctor-fopub/src/dist/docbook-xsl/common.xsl b/vendor/asciidoctor-fopub/src/dist/docbook-xsl/common.xsl new file mode 100644 index 00000000..5459d8c0 --- /dev/null +++ b/vendor/asciidoctor-fopub/src/dist/docbook-xsl/common.xsl @@ -0,0 +1,160 @@ + + + + + #222222 + #005498 + #DDDDDD + normal + #BA3925 + + + #7A2518 + + normal + #EEEEEE + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 0 + + + + + 1 + 0 + + + + + + + + + + + + + + 2 + + + 2 + + + + + 0 + .png + 0 + images/icons/ + 0 + + 0 + + + 0 + #E0E0E0 + + + + + 1 + images/icons/ + .svg + + + 0 + + + + + + + + + + + + + + + + + + + 0 + + + + +article toc,title +book toc,title,figure,table,example,equation + + +article nop +book nop + + + + + diff --git a/vendor/asciidoctor-fopub/src/dist/docbook-xsl/fo-pdf.xsl b/vendor/asciidoctor-fopub/src/dist/docbook-xsl/fo-pdf.xsl new file mode 100644 index 00000000..f83bffbc --- /dev/null +++ b/vendor/asciidoctor-fopub/src/dist/docbook-xsl/fo-pdf.xsl @@ -0,0 +1,973 @@ + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + + + + + Arial,sans-serif + + + + Georgia,serif + + + + Liberation Mono,Courier New,Courier,monospace + + + + + + + + Symbol,ZapfDingbats + + + + + + + + + + + + + + + + + + + + + + + + + false + 1.5 + + justify + 12 + + pt + + + + + + + + + 0 + 0 + 0 + 1em + 0.8em + 1.2em + + + + + + + + + + + .3em .25em .1em .25em + 0 + + + + + + + normal + dotted + dotted + 1pt + #BFBFBF + 0 + .2em + .4em + 1em + 1.2em + 1.4em + false + wrap + false + preserve + preserve + start + + + + + 10pt + start + wrap + + + + + 1 + + + transparent + + + + 1em .5em .75em .5em + + + + + + + A4 + 1 + 1 + 10mm + 10mm + 20mm + 20mm + 15mm + 15mm + 4mm + 6mm + 0 + 0 + 10mm + 10mm + 1 5 1 + + + + 0 + 2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.8em + 1em + 1.2em + 0.8em + 1em + 1.2em + + auto + + + + figure after + example before + table before + + + + + + + + 36pt + + + + #6F6F6F + 18pt + .75pt + solid + + 0 + + + + 12pt + 12pt + + + + 1pt + solid + #E6E6E6 + 12pt + 12pt + 0 + 12pt + 0 + 0 + + + + solid + 1pt + #D9D9D9 + #F2F2F2 + 16pt + 16pt + 18pt + 0 + + + + + + + + pt + + + pt + + + + + + + 4pt + 4pt + 2pt + 2pt + + + 0.3pt + 0.15pt + #5c5c4f + #5c5c4f + white + white + white + white + + + 4pt + 4pt + 2pt + 2pt + + + + + + + 6.3in + 1em + + + + + + 0 + 0 + 0 + 0 + + + + 1 + 0 + + + + + + 0 + 1.0 + + + + 1.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + auto + + + + + + + + + + + + + + + + auto + + + + + + auto + + + + + + + + + + + + + + + + auto + + + + + + auto + + + + + + + + + + + + % + + scale-to-fit + scale-down-to-fit + + + + + + auto + + + + + + + + + + + + % + + scale-to-fit + scale-down-to-fit + + + + + + + + + + + + + + + + + + + + + + + + + before + center + after + auto + + + + + + + + + + + + + + always + 0.8em + 1.0em + 1.2em + 0.8em + 1.0em + 1.2em + left + + + + + + + pt + + + + + + pt + + + + + + pt + + + + + + pt + + + + + + pt + + + + + + pt + + + + + always + + + + + + + + + + + + + + + + + + + false + + + + + + + center + left + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 1 + + + 0.4em + + + + 1.2em + 1em + 1.4em + 1.2em + 1em + 1.4em + + + + 0.5em + 0.2em + 0.8em + + + + bold + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #6F6F6F + + + + + + normal + 12pt + center + + + + + white + 24pt + normal + left + + + + + + + + + + + center + + + + + + + black + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + pt + + + + + + + pt + + + bold + 0 1pt + + + + + + left + + + + + rule + 2in + 0.5pt + + + + + + diff --git a/vendor/asciidoctor-fopub/src/dist/docbook-xsl/fop-config.xml b/vendor/asciidoctor-fopub/src/dist/docbook-xsl/fop-config.xml new file mode 100644 index 00000000..68398a6b --- /dev/null +++ b/vendor/asciidoctor-fopub/src/dist/docbook-xsl/fop-config.xml @@ -0,0 +1,11 @@ + + + true + + + + + + + + diff --git a/vendor/asciidoctor-fopub/src/dist/docbook-xsl/highlight.xsl b/vendor/asciidoctor-fopub/src/dist/docbook-xsl/highlight.xsl new file mode 100644 index 00000000..096cfa20 --- /dev/null +++ b/vendor/asciidoctor-fopub/src/dist/docbook-xsl/highlight.xsl @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl-config.xml b/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl-config.xml new file mode 100644 index 00000000..a7d73c1e --- /dev/null +++ b/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl-config.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl/asciidoc-hl.xml b/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl/asciidoc-hl.xml new file mode 100644 index 00000000..5478b1d6 --- /dev/null +++ b/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl/asciidoc-hl.xml @@ -0,0 +1,41 @@ + + + + + //// + //// + + + // + + + + ^(={1,6} .+)$ + + MULTILINE + + + ^(\.[^\.\s].+)$ + + MULTILINE + + + ^(:!?\w.*?:) + + MULTILINE + + + ^(-|\*{1,5}|\d*\.{1,5})(?= .+$) + + MULTILINE + + + ^(\[.+\])$ + + MULTILINE + + diff --git a/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl/bourne-hl.xml b/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl/bourne-hl.xml new file mode 100644 index 00000000..e2cd98d8 --- /dev/null +++ b/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl/bourne-hl.xml @@ -0,0 +1,95 @@ + + + + # + + << + ' + " + - + + + + + " + \ + + + ' + \ + + + + 0x + + + + . + + + + + + if + then + else + elif + fi + case + esac + for + while + until + do + done + + exec + shift + exit + times + break + export + trap + continue + readonly + wait + eval + return + + cd + echo + hash + pwd + read + set + test + type + ulimit + umask + unset + + diff --git a/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl/c-hl.xml b/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl/c-hl.xml new file mode 100644 index 00000000..7af793e1 --- /dev/null +++ b/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl/c-hl.xml @@ -0,0 +1,117 @@ + + + + + /** + */ + + + + + + + + /* + */ + + // + + + # + \ + + + + + " + \ + + + ' + \ + + + 0x + ul + lu + u + l + + + + . + + e + ul + lu + u + f + l + + + + auto + _Bool + break + case + char + _Complex + const + continue + default + do + double + else + enum + extern + float + for + goto + if + _Imaginary + inline + int + long + register + restrict + return + short + signed + sizeof + static + struct + switch + typedef + union + unsigned + void + volatile + while + + \ No newline at end of file diff --git a/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl/cpp-hl.xml b/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl/cpp-hl.xml new file mode 100644 index 00000000..d3156cbd --- /dev/null +++ b/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl/cpp-hl.xml @@ -0,0 +1,151 @@ + + + + + /** + */ + + + + + + + + /* + */ + + // + + + # + \ + + + + + " + \ + + + ' + \ + + + 0x + ul + lu + u + l + + + + . + + e + ul + lu + u + f + l + + + + + auto + _Bool + break + case + char + _Complex + const + continue + default + do + double + else + enum + extern + float + for + goto + if + _Imaginary + inline + int + long + register + restrict + return + short + signed + sizeof + static + struct + switch + typedef + union + unsigned + void + volatile + while + + asm + dynamic_cast + namespace + reinterpret_cast + try + bool + explicit + new + static_cast + typeid + catch + false + operator + template + typename + class + friend + private + this + using + const_cast + inline + public + throw + virtual + delete + mutable + protected + true + wchar_t + + \ No newline at end of file diff --git a/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl/csharp-hl.xml b/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl/csharp-hl.xml new file mode 100644 index 00000000..5cf917b2 --- /dev/null +++ b/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl/csharp-hl.xml @@ -0,0 +1,194 @@ + + + + + /** + */ + + + + /// + + + + /* + */ + + // + + + [ + ] + ( + ) + + + + # + \ + + + + + + @" + " + \ + + + + " + \ + + + ' + \ + + + 0x + ul + lu + u + l + + + + . + + e + ul + lu + u + f + d + m + l + + + + abstract + as + base + bool + break + byte + case + catch + char + checked + class + const + continue + decimal + default + delegate + do + double + else + enum + event + explicit + extern + false + finally + fixed + float + for + foreach + goto + if + implicit + in + int + interface + internal + is + lock + long + namespace + new + null + object + operator + out + override + params + private + protected + public + readonly + ref + return + sbyte + sealed + short + sizeof + stackalloc + static + string + struct + switch + this + throw + true + try + typeof + uint + ulong + unchecked + unsafe + ushort + using + virtual + void + volatile + while + + + + add + alias + from + get + global + group + into + join + orderby + partial + remove + select + set + value + where + yield + + \ No newline at end of file diff --git a/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl/css-hl.xml b/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl/css-hl.xml new file mode 100644 index 00000000..ffbb7894 --- /dev/null +++ b/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl/css-hl.xml @@ -0,0 +1,176 @@ + + + + + /* + */ + + + " + \ + + + + ' + \ + + + + . + + + + @charset + @import + @media + @page + + + + - + azimuth + background-attachment + background-color + background-image + background-position + background-repeat + background + border-collapse + border-color + border-spacing + border-style + border-top + border-right + border-bottom + border-left + border-top-color + border-right-color + border-bottom-color + border-left-color + border-top-style + border-right-style + border-bottom-style + border-left-style + border-top-width + border-right-width + border-bottom-width + border-left-width + border-width + border + bottom + caption-side + clear + clip + color + content + counter-increment + counter-reset + cue-after + cue-before + cue + cursor + direction + display + elevation + empty-cells + float + font-family + font-size + font-style + font-variant + font-weight + font + height + left + letter-spacing + line-height + list-style-image + list-style-position + list-style-type + list-style + margin-right + margin-left + margin-top + margin-bottom + margin + max-height + max-width + min-height + min-width + orphans + outline-color + outline-style + outline-width + outline + overflow + padding-top + padding-right + padding-bottom + padding-left + padding + page-break-after + page-break-before + page-break-inside + pause-after + pause-before + pause + pitch-range + pitch + play-during + position + quotes + richness + right + speak-header + speak-numeral + speak-punctuation + speak + speech-rate + stress + table-layout + text-align + text-decoration + text-indent + text-transform + top + unicode-bidi + vertical-align + visibility + voice-family + volume + white-space + widows + width + word-spacing + z-index + + diff --git a/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl/html-hl.xml b/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl/html-hl.xml new file mode 100644 index 00000000..5b6761ba --- /dev/null +++ b/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl/html-hl.xml @@ -0,0 +1,122 @@ + + + + + + + a + abbr + address + area + article + aside + audio + b + base + bdi + blockquote + body + br + button + caption + canvas + cite + code + command + col + colgroup + dd + del + dialog + div + dl + dt + em + embed + fieldset + figcaption + figure + font + form + footer + h1 + h2 + h3 + h4 + h5 + h6 + head + header + hr + html + i + iframe + img + input + ins + kbd + label + legend + li + link + map + mark + menu + menu + meta + nav + noscript + object + ol + optgroup + option + p + param + pre + q + samp + script + section + select + small + source + span + strong + style + sub + summary + sup + table + tbody + td + textarea + tfoot + th + thead + time + title + tr + track + u + ul + var + video + wbr + xmp + + + + + xsl: + + + diff --git a/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl/ini-hl.xml b/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl/ini-hl.xml new file mode 100644 index 00000000..84be9c68 --- /dev/null +++ b/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl/ini-hl.xml @@ -0,0 +1,45 @@ + + + + ; + + + ^(\[.+\]\s*)$ + + MULTILINE + + + + ^(.+)(?==) + + MULTILINE + + \ No newline at end of file diff --git a/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl/java-hl.xml b/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl/java-hl.xml new file mode 100644 index 00000000..d499d83f --- /dev/null +++ b/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl/java-hl.xml @@ -0,0 +1,117 @@ + + + + + /** + */ + + + + /* + */ + + // + + " + \ + + + ' + \ + + + @ + ( + ) + + + 0x + + + + . + e + f + d + l + + + + abstract + boolean + break + byte + case + catch + char + class + const + continue + default + do + double + else + extends + final + finally + float + for + goto + if + implements + import + instanceof + int + interface + long + native + new + package + private + protected + public + return + short + static + strictfp + super + switch + synchronized + this + throw + throws + transient + try + void + volatile + while + + \ No newline at end of file diff --git a/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl/javascript-hl.xml b/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl/javascript-hl.xml new file mode 100644 index 00000000..5749b887 --- /dev/null +++ b/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl/javascript-hl.xml @@ -0,0 +1,147 @@ + + + + + /* + */ + + // + + " + \ + + + ' + \ + + + 0x + + + + . + e + + + + break + case + catch + continue + default + delete + do + else + finally + for + function + if + in + instanceof + new + return + switch + this + throw + try + typeof + var + void + while + with + + abstract + boolean + byte + char + class + const + debugger + double + enum + export + extends + final + float + goto + implements + import + int + interface + long + native + package + private + protected + public + short + static + super + synchronized + throws + transient + volatile + + + prototype + + Array + Boolean + Date + Error + EvalError + Function + Math + Number + Object + RangeError + ReferenceError + RegExp + String + SyntaxError + TypeError + URIError + + decodeURI + decodeURIComponent + encodeURI + encodeURIComponent + eval + isFinite + isNaN + parseFloat + parseInt + + Infinity + NaN + undefined + + \ No newline at end of file diff --git a/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl/perl-hl.xml b/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl/perl-hl.xml new file mode 100644 index 00000000..23fdfd82 --- /dev/null +++ b/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl/perl-hl.xml @@ -0,0 +1,120 @@ + + + + # + + << + ' + " + + + + " + \ + + + ' + \ + + + + 0x + + + + . + + + + + if + unless + while + until + foreach + else + elsif + for + when + default + given + + caller + continue + die + do + dump + eval + exit + goto + last + next + redo + return + sub + wantarray + + caller + import + local + my + package + use + + do + import + no + package + require + use + + bless + dbmclose + dbmopen + package + ref + tie + tied + untie + use + + and + or + not + eq + ne + lt + gt + le + ge + cmp + + \ No newline at end of file diff --git a/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl/php-hl.xml b/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl/php-hl.xml new file mode 100644 index 00000000..a0b1df8d --- /dev/null +++ b/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl/php-hl.xml @@ -0,0 +1,154 @@ + + + + + /** + */ + + + + + + + + /* + */ + + // + # + + " + \ + + + + ' + \ + + + + <<< + + + 0x + + + + . + e + + + + and + or + xor + __FILE__ + exception + __LINE__ + array + as + break + case + class + const + continue + declare + default + die + do + echo + else + elseif + empty + enddeclare + endfor + endforeach + endif + endswitch + endwhile + eval + exit + extends + for + foreach + function + global + if + include + include_once + isset + list + new + print + require + require_once + return + static + switch + unset + use + var + while + __FUNCTION__ + __CLASS__ + __METHOD__ + final + php_user_filter + interface + implements + extends + public + private + protected + abstract + clone + try + catch + throw + cfunction + old_function + true + false + + namespace + __NAMESPACE__ + goto + __DIR__ + + + + + ?> + <?php + <?= + + + \ No newline at end of file diff --git a/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl/python-hl.xml b/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl/python-hl.xml new file mode 100644 index 00000000..1b1f0873 --- /dev/null +++ b/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl/python-hl.xml @@ -0,0 +1,100 @@ + + + + + + @ + ( + ) + + # + + """ + + + + ''' + + + + " + \ + + + ' + \ + + + 0x + l + + + + . + + e + l + + + + and + del + from + not + while + as + elif + global + or + with + assert + else + if + pass + yield + break + except + import + print + class + exec + in + raise + continue + finally + is + return + def + for + lambda + try + + \ No newline at end of file diff --git a/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl/ruby-hl.xml b/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl/ruby-hl.xml new file mode 100644 index 00000000..2f743528 --- /dev/null +++ b/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl/ruby-hl.xml @@ -0,0 +1,109 @@ + + + + # + + << + + + + " + \ + + + %Q{ + } + \ + + + %/ + / + \ + + + ' + \ + + + %q{ + } + \ + + + 0x + + + + . + e + + + + alias + and + BEGIN + begin + break + case + class + def + defined + do + else + elsif + END + end + ensure + false + for + if + in + module + next + nil + not + or + redo + rescue + retry + return + self + super + then + true + undef + unless + until + when + while + yield + + \ No newline at end of file diff --git a/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl/sql2003-hl.xml b/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl/sql2003-hl.xml new file mode 100644 index 00000000..ac1d5d04 --- /dev/null +++ b/vendor/asciidoctor-fopub/src/dist/docbook-xsl/xslthl/sql2003-hl.xml @@ -0,0 +1,565 @@ + + + + -- + + /* + */ + + + ' + + + + U' + ' + + + + B' + ' + + + + N' + ' + + + + X' + ' + + + + . + + e + + + + + + A + ABS + ABSOLUTE + ACTION + ADA + ADMIN + AFTER + ALWAYS + ASC + ASSERTION + ASSIGNMENT + ATTRIBUTE + ATTRIBUTES + AVG + BEFORE + BERNOULLI + BREADTH + C + CARDINALITY + CASCADE + CATALOG_NAME + CATALOG + CEIL + CEILING + CHAIN + CHAR_LENGTH + CHARACTER_LENGTH + CHARACTER_SET_CATALOG + CHARACTER_SET_NAME + CHARACTER_SET_SCHEMA + CHARACTERISTICS + CHARACTERS + CHECKED + CLASS_ORIGIN + COALESCE + COBOL + CODE_UNITS + COLLATION_CATALOG + COLLATION_NAME + COLLATION_SCHEMA + COLLATION + COLLECT + COLUMN_NAME + COMMAND_FUNCTION_CODE + COMMAND_FUNCTION + COMMITTED + CONDITION_NUMBER + CONDITION + CONNECTION_NAME + CONSTRAINT_CATALOG + CONSTRAINT_NAME + CONSTRAINT_SCHEMA + CONSTRAINTS + CONSTRUCTORS + CONTAINS + CONVERT + CORR + COUNT + COVAR_POP + COVAR_SAMP + CUME_DIST + CURRENT_COLLATION + CURSOR_NAME + DATA + DATETIME_INTERVAL_CODE + DATETIME_INTERVAL_PRECISION + DEFAULTS + DEFERRABLE + DEFERRED + DEFINED + DEFINER + DEGREE + DENSE_RANK + DEPTH + DERIVED + DESC + DESCRIPTOR + DIAGNOSTICS + DISPATCH + DOMAIN + DYNAMIC_FUNCTION_CODE + DYNAMIC_FUNCTION + EQUALS + EVERY + EXCEPTION + EXCLUDE + EXCLUDING + EXP + EXTRACT + FINAL + FIRST + FLOOR + FOLLOWING + FORTRAN + FOUND + FUSION + G + GENERAL + GO + GOTO + GRANTED + HIERARCHY + IMPLEMENTATION + INCLUDING + INCREMENT + INITIALLY + INSTANCE + INSTANTIABLE + INTERSECTION + INVOKER + ISOLATION + K + KEY_MEMBER + KEY_TYPE + KEY + LAST + LENGTH + LEVEL + LN + LOCATOR + LOWER + M + MAP + MATCHED + MAX + MAXVALUE + MESSAGE_LENGTH + MESSAGE_OCTET_LENGTH + MESSAGE_TEXT + MIN + MINVALUE + MOD + MORE + MUMPS + NAME + NAMES + NESTING + NEXT + NORMALIZE + NORMALIZED + NULLABLE + NULLIF + NULLS + NUMBER + OBJECT + OCTET_LENGTH + OCTETS + OPTION + OPTIONS + ORDERING + ORDINALITY + OTHERS + OVERLAY + OVERRIDING + PAD + PARAMETER_MODE + PARAMETER_NAME + PARAMETER_ORDINAL_POSITION + PARAMETER_SPECIFIC_CATALOG + PARAMETER_SPECIFIC_NAME + PARAMETER_SPECIFIC_SCHEMA + PARTIAL + PASCAL + PATH + PERCENT_RANK + PERCENTILE_CONT + PERCENTILE_DISC + PLACING + PLI + POSITION + POWER + PRECEDING + PRESERVE + PRIOR + PRIVILEGES + PUBLIC + RANK + READ + RELATIVE + REPEATABLE + RESTART + RETURNED_CARDINALITY + RETURNED_LENGTH + RETURNED_OCTET_LENGTH + RETURNED_SQLSTATE + ROLE + ROUTINE_CATALOG + ROUTINE_NAME + ROUTINE_SCHEMA + ROUTINE + ROW_COUNT + ROW_NUMBER + SCALE + SCHEMA_NAME + SCHEMA + SCOPE_CATALOG + SCOPE_NAME + SCOPE_SCHEMA + SECTION + SECURITY + SELF + SEQUENCE + SERIALIZABLE + SERVER_NAME + SESSION + SETS + SIMPLE + SIZE + SOURCE + SPACE + SPECIFIC_NAME + SQRT + STATE + STATEMENT + STDDEV_POP + STDDEV_SAMP + STRUCTURE + STYLE + SUBCLASS_ORIGIN + SUBSTRING + SUM + TABLE_NAME + TABLESAMPLE + TEMPORARY + TIES + TOP_LEVEL_COUNT + TRANSACTION_ACTIVE + TRANSACTION + TRANSACTIONS_COMMITTED + TRANSACTIONS_ROLLED_BACK + TRANSFORM + TRANSFORMS + TRANSLATE + TRIGGER_CATALOG + TRIGGER_NAME + TRIGGER_SCHEMA + TRIM + TYPE + UNBOUNDED + UNCOMMITTED + UNDER + UNNAMED + USAGE + USER_DEFINED_TYPE_CATALOG + USER_DEFINED_TYPE_CODE + USER_DEFINED_TYPE_NAME + USER_DEFINED_TYPE_SCHEMA + VIEW + WORK + WRITE + ZONE + + ADD + ALL + ALLOCATE + ALTER + AND + ANY + ARE + ARRAY + AS + ASENSITIVE + ASYMMETRIC + AT + ATOMIC + AUTHORIZATION + BEGIN + BETWEEN + BIGINT + BINARY + BLOB + BOOLEAN + BOTH + BY + CALL + CALLED + CASCADED + CASE + CAST + CHAR + CHARACTER + CHECK + CLOB + CLOSE + COLLATE + COLUMN + COMMIT + CONNECT + CONSTRAINT + CONTINUE + CORRESPONDING + CREATE + CROSS + CUBE + CURRENT_DATE + CURRENT_DEFAULT_TRANSFORM_GROUP + CURRENT_PATH + CURRENT_ROLE + CURRENT_TIME + CURRENT_TIMESTAMP + CURRENT_TRANSFORM_GROUP_FOR_TYPE + CURRENT_USER + CURRENT + CURSOR + CYCLE + DATE + DAY + DEALLOCATE + DEC + DECIMAL + DECLARE + DEFAULT + DELETE + DEREF + DESCRIBE + DETERMINISTIC + DISCONNECT + DISTINCT + DOUBLE + DROP + DYNAMIC + EACH + ELEMENT + ELSE + END + END-EXEC + ESCAPE + EXCEPT + EXEC + EXECUTE + EXISTS + EXTERNAL + FALSE + FETCH + FILTER + FLOAT + FOR + FOREIGN + FREE + FROM + FULL + FUNCTION + GET + GLOBAL + GRANT + GROUP + GROUPING + HAVING + HOLD + HOUR + IDENTITY + IMMEDIATE + IN + INDICATOR + INNER + INOUT + INPUT + INSENSITIVE + INSERT + INT + INTEGER + INTERSECT + INTERVAL + INTO + IS + ISOLATION + JOIN + LANGUAGE + LARGE + LATERAL + LEADING + LEFT + LIKE + LOCAL + LOCALTIME + LOCALTIMESTAMP + MATCH + MEMBER + MERGE + METHOD + MINUTE + MODIFIES + MODULE + MONTH + MULTISET + NATIONAL + NATURAL + NCHAR + NCLOB + NEW + NO + NONE + NOT + NULL + NUMERIC + OF + OLD + ON + ONLY + OPEN + OR + ORDER + OUT + OUTER + OUTPUT + OVER + OVERLAPS + PARAMETER + PARTITION + PRECISION + PREPARE + PRIMARY + PROCEDURE + RANGE + READS + REAL + RECURSIVE + REF + REFERENCES + REFERENCING + REGR_AVGX + REGR_AVGY + REGR_COUNT + REGR_INTERCEPT + REGR_R2 + REGR_SLOPE + REGR_SXX + REGR_SXY + REGR_SYY + RELEASE + RESULT + RETURN + RETURNS + REVOKE + RIGHT + ROLLBACK + ROLLUP + ROW + ROWS + SAVEPOINT + SCROLL + SEARCH + SECOND + SELECT + SENSITIVE + SESSION_USER + SET + SIMILAR + SMALLINT + SOME + SPECIFIC + SPECIFICTYPE + SQL + SQLEXCEPTION + SQLSTATE + SQLWARNING + START + STATIC + SUBMULTISET + SYMMETRIC + SYSTEM_USER + SYSTEM + TABLE + THEN + TIME + TIMESTAMP + TIMEZONE_HOUR + TIMEZONE_MINUTE + TO + TRAILING + TRANSLATION + TREAT + TRIGGER + TRUE + UESCAPE + UNION + UNIQUE + UNKNOWN + UNNEST + UPDATE + UPPER + USER + USING + VALUE + VALUES + VAR_POP + VAR_SAMP + VARCHAR + VARYING + WHEN + WHENEVER + WHERE + WIDTH_BUCKET + WINDOW + WITH + WITHIN + WITHOUT + YEAR + + diff --git a/vendor/asciidoctor-fopub/src/main/java/org/apache/fop/cli/InputHandler.java b/vendor/asciidoctor-fopub/src/main/java/org/apache/fop/cli/InputHandler.java new file mode 100644 index 00000000..35706b96 --- /dev/null +++ b/vendor/asciidoctor-fopub/src/main/java/org/apache/fop/cli/InputHandler.java @@ -0,0 +1,332 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.cli; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Vector; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParserFactory; +import javax.xml.transform.ErrorListener; +import javax.xml.transform.Result; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.URIResolver; +import javax.xml.transform.sax.SAXResult; +import javax.xml.transform.sax.SAXSource; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; + +import org.xml.sax.EntityResolver; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.apache.fop.ResourceEventProducer; +import org.apache.fop.apps.FOPException; +import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.apps.Fop; +import org.apache.fop.apps.FopFactory; +import org.apache.fop.render.awt.viewer.Renderable; + +/** + * Class for handling files input from command line + * either with XML and XSLT files (and optionally xsl + * parameters) or FO File input alone. + */ +public class InputHandler implements ErrorListener, Renderable { + + /** original source file */ + protected File sourcefile; + private File stylesheet; // for XML/XSLT usage + private Vector xsltParams; // for XML/XSLT usage + private EntityResolver entityResolver = null; + private URIResolver uriResolver = null; + + /** the logger */ + protected Log log = LogFactory.getLog(InputHandler.class); + + /** + * Constructor for XML->XSLT->FO input + * + * @param xmlfile XML file + * @param xsltfile XSLT file + * @param params Vector of command-line parameters (name, value, + * name, value, ...) for XSL stylesheet, null if none + */ + public InputHandler(File xmlfile, File xsltfile, Vector params) { + sourcefile = xmlfile; + stylesheet = xsltfile; + xsltParams = params; + } + + /** + * Constructor for FO input + * @param fofile the file to read the FO document. + */ + public InputHandler(File fofile) { + sourcefile = fofile; + } + + /** + * Generate a document, given an initialized Fop object + * @param userAgent the user agent + * @param outputFormat the output format to generate (MIME type, see MimeConstants) + * @param out the output stream to write the generated output to (may be null if not applicable) + * @throws FOPException in case of an error during processing + */ + public void renderTo(FOUserAgent userAgent, String outputFormat, OutputStream out) + throws FOPException { + + FopFactory factory = userAgent.getFactory(); + Fop fop; + if (out != null) { + fop = factory.newFop(outputFormat, userAgent, out); + } else { + fop = factory.newFop(outputFormat, userAgent); + } + + // if base URL was not explicitly set in FOUserAgent, obtain here + if (fop.getUserAgent().getBaseURL() == null && sourcefile != null) { + String baseURL = null; + + try { + baseURL = new File(sourcefile.getAbsolutePath()) + .getParentFile().toURI().toURL().toExternalForm(); + } catch (Exception e) { + baseURL = ""; + } + fop.getUserAgent().setBaseURL(baseURL); + } + + // Resulting SAX events (the generated FO) must be piped through to FOP + Result res = new SAXResult(fop.getDefaultHandler()); + + transformTo(res); + } + + /** {@inheritDoc} */ + public void renderTo(FOUserAgent userAgent, String outputFormat) throws FOPException { + renderTo(userAgent, outputFormat, null); + } + + /** + * In contrast to render(Fop) this method only performs the XSLT stage and saves the + * intermediate XSL-FO file to the output file. + * @param out OutputStream to write the transformation result to. + * @throws FOPException in case of an error during processing + */ + public void transformTo(OutputStream out) throws FOPException { + Result res = new StreamResult(out); + transformTo(res); + } + + /** + * Creates a Source for the main input file. Processes XInclude if + * available in the XML parser. + * + * @return the Source for the main input file + */ + protected Source createMainSource() { + Source source; + InputStream in; + String uri; + if (this.sourcefile != null) { + try { + in = new java.io.FileInputStream(this.sourcefile); + uri = this.sourcefile.toURI().toASCIIString(); + } catch (FileNotFoundException e) { + //handled elsewhere + return new StreamSource(this.sourcefile); + } + } else { + in = System.in; + uri = null; + } + try { + InputSource is = new InputSource(in); + is.setSystemId(uri); + XMLReader xr = getXMLReader(); + if (entityResolver != null) { + xr.setEntityResolver(entityResolver); + } + source = new SAXSource(xr, is); + } catch (SAXException e) { + if (this.sourcefile != null) { + source = new StreamSource(this.sourcefile); + } else { + source = new StreamSource(in, uri); + } + } catch (ParserConfigurationException e) { + if (this.sourcefile != null) { + source = new StreamSource(this.sourcefile); + } else { + source = new StreamSource(in, uri); + } + } + return source; + } + + /** + * Creates a catalog resolver and uses it for XML parsing and XSLT URI resolution. + * Tries the Apache Commons Resolver, and if unsuccessful, + * tries the same built into Java 6. + * @param userAgent the user agent instance + */ + public void createCatalogResolver(FOUserAgent userAgent) { + String[] classNames = new String[] { + "org.apache.xml.resolver.tools.CatalogResolver", + "com.sun.org.apache.xml.internal.resolver.tools.CatalogResolver"}; + ResourceEventProducer eventProducer + = ResourceEventProducer.Provider.get(userAgent.getEventBroadcaster()); + Class resolverClass = null; + for (int i = 0; i < classNames.length && resolverClass == null; ++i) { + try { + resolverClass = Class.forName(classNames[i]); + } catch (ClassNotFoundException e) { + // No worries + } + } + if (resolverClass == null) { + eventProducer.catalogResolverNotFound(this); + return; + } + try { + entityResolver = (EntityResolver) resolverClass.newInstance(); + uriResolver = (URIResolver) resolverClass.newInstance(); + } catch (InstantiationException e) { + log.error("Error creating the catalog resolver: " + e.getMessage()); + eventProducer.catalogResolverNotCreated(this, e.getMessage()); + } catch (IllegalAccessException e) { + log.error("Error creating the catalog resolver: " + e.getMessage()); + eventProducer.catalogResolverNotCreated(this, e.getMessage()); + } + } + + /** + * Creates a Source for the selected stylesheet. + * + * @return the Source for the selected stylesheet or null if there's no stylesheet + */ + protected Source createXSLTSource() { + Source xslt = null; + if (this.stylesheet != null) { + if (entityResolver != null) { + try { + InputSource is = new InputSource(this.stylesheet.getPath()); + XMLReader xr = getXMLReader(); + xr.setEntityResolver(entityResolver); + xslt = new SAXSource(xr, is); + } catch (SAXException e) { + // return StreamSource + } catch (ParserConfigurationException e) { + // return StreamSource + } + } + if (xslt == null) { + xslt = new StreamSource(this.stylesheet); + } + } + return xslt; + } + + private XMLReader getXMLReader() throws ParserConfigurationException, SAXException { + SAXParserFactory spf = SAXParserFactory.newInstance(); + spf.setFeature("http://xml.org/sax/features/namespaces", true); + spf.setFeature("http://apache.org/xml/features/xinclude", true); + XMLReader xr = spf.newSAXParser().getXMLReader(); + return xr; + } + + /** + * Transforms the input document to the input format expected by FOP using XSLT. + * @param result the Result object where the result of the XSL transformation is sent to + * @throws FOPException in case of an error during processing + */ + protected void transformTo(Result result) throws FOPException { + try { + // Setup XSLT + TransformerFactory factory = TransformerFactory.newInstance(); + if (uriResolver != null) { + factory.setURIResolver(uriResolver); + } + factory.setErrorListener(this); + Transformer transformer; + + Source xsltSource = createXSLTSource(); + if (xsltSource == null) { // FO Input + transformer = factory.newTransformer(); + } else { // XML/XSLT input + transformer = factory.newTransformer(xsltSource); + + // Set the value of parameters, if any, defined for stylesheet + if (xsltParams != null) { + for (int i = 0; i < xsltParams.size(); i += 2) { + transformer.setParameter((String) xsltParams.elementAt(i), + (String) xsltParams.elementAt(i + 1)); + } + } + } + transformer.setErrorListener(this); + + // Create a SAXSource from the input Source file + Source src = createMainSource(); + + // Start XSLT transformation and FOP processing + transformer.transform(src, result); + + } catch (Exception e) { + throw new FOPException(e); + } + } + + // --- Implementation of the ErrorListener interface --- + + /** + * {@inheritDoc} + */ + public void warning(TransformerException exc) { + log.warn(exc.getLocalizedMessage()); + } + + /** + * {@inheritDoc} + */ + public void error(TransformerException exc) { + log.error(exc.toString()); + } + + /** + * {@inheritDoc} + */ + public void fatalError(TransformerException exc) + throws TransformerException { + throw exc; + } + +}