2016年11月8日

社内勉強会でWeb技術について喋りました

ちょっと時間が開いてしまいましたが、10月の頭に社内勉強会を開催し、最近のフロントエンド開発について喋りました。
アクティブに最新情報を追って行かないといけないのがフロントエンド開発なので、そこそこ評判は良かったように思います。

当日は、「HTML5の新機能紹介」「フロントエンド開発環境の話」という2つを喋りましたので、発表資料を置いておきます。

HTML5の新機能について



これは主に非開発者向けの内容になっていて、HTML5でのグラフィックやメディア系の内容をまとめてあります。
JavaScriptでWebGLが使えること、動画プレイヤをCSSなどでカスタマイズできること等が特に受けが良かったです。
まだモダンブラウザを使えば、大抵のブラウザで利用できるようになってきたため、その点も評判は良いように思いました。

フロントエンド開発環境の話



こちらは開発者向けの内容になっていて、JavaScriptをビルドするための環境周りについてのお話になります。
gulpjsとwebpackを使って、JavaScriptをビルドしてサイト一式を構築する点については、うまくメリットの説明ができなかったと思うので、また機会があれば色々な視点からまとめていきたいと思いました。

以上、今後もちょくちょくこういった社内勉強会をやっていきたいと思います。

2015年1月13日

RaspberryPi + Webカメラ + GrovePi を試してみた

はじめに

Raspberry Piは安価(モデルによりますが3000円~5000円程度)で小型ながらもLinuxが動作するコンピュータであり、GPIOやI2Cといった各種I/Oを備えていることから、ロボット制御や簡易サーバに用いられています。

http://www.raspberrypi.org/

今回はRaspberry Piを使って、簡単な実験を行ってみたいと思います。
実験ではカメラとセンサー・制御デバイスを使ってみたかったので、以下のものを用意しました。

  • Raspberry Pi 本体
  • Webカメラ(Logicool製のものを用いました)
  • GrovePi 本体
  • Groveセンサー(今回は光センサと温度・湿度センサ)
  • LED(青,赤,緑)と抵抗
  • ブレッドボード、ジャンパ線
  • USBシリアルケーブル(無くてもよいがあったほうが便利)


GroveセンサーはArduinoプラットフォームでよく利用されているセンサー類や制御デバイス類であり、GrovePi は Groveセンサーを Raspberry Pi に接続するためのボードです。

http://www.dexterindustries.com/GrovePi/

GroveセンサーやGrovePiを扱っているところは少なそうですが、秋葉原や通販で入手可能なようです(私は秋葉原で購入しました)。

全体構想

ざっくりとした全体構想としては以下のようなものを考えました。

  • Raspberry PiにWebカメラを接続して、リモートから動画が見れる。
  • Grove Piに各種センサーを接続して、環境モニタリングができる。
  • LEDやモーターを接続して、遠隔からデバイス制御が行える。
  • ユーザーインターフェースはWebブラウザを用いる。


さっそくハードウェアおよびソフトウェア構成を検討したいと思います。

ハードウェア構成

Raspberry Pi と各種ハードウェアは以下のように接続しました。

RaspberryPi 本体
  + Web カメラ (Raspberry Pi と USB 接続)
  + GrovePi (Raspberry Pi の I/O ピンに直接接続)
    + 光センサー (GrovePi の A0 コネクタに接続
    + DHT11 温度・湿度センサー(GrovePi の D5 コネクタに接続)
    + LED (GrovePi の D2~D4 コネクタに接続)

全て接続すると以下の様な感じになります(Webカメラが写ってませんが...)。



ブレッドボード上には赤・緑・青のLEDと抵抗を差し込んでGrovePiのデジタル用コネクタに接続しています(写真では青が点灯しています)。また写真の右上にあるのが光センサー(左)と温度・湿度センサー(右)です。

ソフトウェア構成

動画ストリーミング

Rasberry PiにUSBで接続したWebカメラから、ブラウザで閲覧可能な形式でストリーミング配信できるソフトウェアを検討したところ mjpg-streaming というソフトウェアが適しているようでした。

http://sourceforge.net/projects/mjpg-streamer/

ソースコードからビルドしたあと、例えば以下のように mjpg_streamer コマンドを実行することで HTTP による動画配信が開始されます。

$ sudo ./mjpg_streamer -i "./input_uvc.so -f 30 -r 320x240 -d /dev/video0" -o "./output_http.so -w ./www -p 8080"


GrovePi ライブラリ

GrovePi に接続したセンサーや制御デバイスにアクセスするための、Python 用ライブラリが提供されているため、これを用います。

http://www.dexterindustries.com/GrovePi/engineering/python-library-documentation/

例えば、D2 に接続した LED の点灯は以下のように簡単にできます。

import grovepi
grovepi.pinMode(2, "OUTPUT")
grovepi.digitalWrite(2, 1)

なお grovepi ライブラリを import した時点でハードウェアにアクセスするようです。そのため、この python プログラムは root 権限(sudo)で起動する必要があります。


サーバサイド

ユーザインターフェースとしてはWebブラウザを想定しているので、サーバサイドには軽量なWebフレームワークを利用したいと思います。今回の実験ではデータベース連携なども不要なので Flask を利用することにしました。

http://flask.pocoo.org/

簡単に Web アプリを作成でき、例えば HTTP で '/' にGETアクセスされた時に、用意してあるテンプレートファイル 'index.html' を返すだけなら以下のように書けます。

from flask import Flask, render_template
app = Flask(__name__)

@app.route('/')
def index():
    return render_template('index.html')

if __name__ == '__main__':
    app.run(host = '0.0.0.0')

センサー値取得

GrovePi に接続したセンサーから値を取得するのに GrovePi の Python ライブラリを利用します。
  • 光センサーは A0 コネクタに接続しているので、grovepi.analogRead(0) で値が取得できます。
  • 温度・湿度センサーからの値取得には grovepi.dht() という専用の関数を利用します。センサーは D5 コネクタに接続したので、grovepi.dht(5,0) で値が取得できます。第二引数で温度・湿度センサーの種類を指定していますが、今回利用したセンサー(DHT11)では 0 を指定します。
/sensor という URL に GET アクセスすると、各種センサーの値を取得して JSON 形式で返す関数を用意します。

@app.route('/sensor')
def sensor():
    light = grovepi.analogRead(0)
    dht = grovepi.dht(5,0)
    return jsonify(light=light, dht=dht)

JSON 形式でレスポンスを返すため、Flask が提供する jsonify() 関数を用いています。

デバイス制御

デバイス制御にも GrovePi の Python ライブラリを利用しています。/ctrl/led/red や /ctrl/led/green など LED 毎に割り当てた URL に PUT または POST アクセスがあった場合に、LED を点灯(ctrl == 'on')または消灯(ctrl != 'on')させるコードは例えば以下のようになります。

@app.route('/ctrl/led/', methods=['PUT', 'POST'])
def ctrl_led(color=None):
    led_pin = dict(red=2, green=3, blue=4)
    ctrl = request.form.get('ctrl')
    led_on = 1 if ctrl == 'on' else 0
    grovepi.digitalWrite(led_pin[color], led_on)
    return jsonify(led=color, var=led_on)

ここでも結果は jsonify() 関数を使って JSON 形式で返しています。

クライアントサイド

ブラウザ側ではボタンが押されたときにデバイス制御を行ったり、定期的にセンサーの値を取得するために、Ajax でサーバにリクエストを送信することで実現しています。クライアントサイドでは jQuery ライブラリを利用していますので、以下のようにイベントハンドラで Ajax の発行を行っています。

$('.led-ctrl-red').on('mousedown mouseup', function(e){
    $.ajax({url: '/ctrl/led/red', data: {ctrl: e.type == 'mousedown' ? 'on' : 'off'}});
})

この例ではマウスを押下したときと離した時に、Ajax リクエストを発行しています。

動作確認

少し分かりにくいですが、動作させている様子です。

GREEN ボタンを押下している最中のスクリーンショットで、緑の LED が点灯していることが分かるかと思います。また、光センサーや温度・湿度センサーの値も毎秒取得して表示しています。温度は26度・湿度34度であることは分かりますが、光センサーはアナログの入力値をそのまま表示しているため、程度は分かりません。ただ、物をかぶせて暗くすると数値は下がるので、強弱の遷移などは分かりそうです。

 あと静止画のキャプチャなので分かりませんが、背景画像はWebカメラのストリーミングを表示しています。mjpg-streamer は非力な Raspberry Pi でも大きな遅延なく動画配信できているので、なかなか優秀ですね。

まとめ

RaspberryPi に Webカメラや GrovePi を接続して、ブラウザ上で動画やセンサー情報を見ながら、デバイス制御が出来ることは確認できました。これをベースにもう少し複雑なデバイス制御や、他のセンサーとの連携などの発展を考えていきたいと思います。

2014年7月15日

シリコンバレーに行ってきた

こんにちは、開発部の溝江です。
6/16~6/20の期間にシリコンバレーに行ってきました。


シリコンバレー旅行の経緯


弊社ではMIJSという団体に加入しています。
MIJSがどういう団体なのか、ということについてはホームページを見てもらうのが一番良いと思いますが、簡単に言えば
「世界に通用するソフトウェア」
の開発を目指す団体です。

日本企業で開発されるソフトウェアは、日本国内ではそれなりのシェアを獲得することはあっても、その後他国で展開できるパターンは多くありません。そのうちに海外の製品が黒船のように現れ、国内のデファクトスタンダードの地位を奪われる、という展開になってしまうというわけです。
その中でもシリコンバレーには世界の時価総額ランキングに名を連ねる大企業がたくさん集まっています。すると日本で働いている身としては

「やはりシリコンバレーに行かないとだめなのだろうか?」
「日本とシリコンバレーは何が違うのか?」

といった疑問が湧いてきます。
今回はシリコンバレーのベンチャー企業とシリコンバレーで働く日本人エンジニアを訪問し、お話をしました。具体的にはRunWayZendeskBoxTreasure DataTwitterFacebookIP Infusionという豪華なラインナップでした。
日本とシリコンバレーでどのような違いがあるのか、感じたことを以下に書き記してみたいと思います。


開発プロセスの違い


Web系のクラウドサービスを展開している会社はアジャイル開発をやっているところが多かったですね。
例えばスクラムをやっている会社の場合、1~2週間のスプリントを設定し、
毎日スタンドアップミーティングを行う。
日本でも広く認識されている(であろう)やり方が実践されていました。
もちろん、別にアジャイルを知っているからエンジニアのレベルが同じというわけではありません。
アジャイル開発を実際に行うためには自分の組織にあった形態を探り、継続的に改善していく必要があるでしょう。知っていることと実行することは大きく違います。
しかし少なくともシリコンバレーでは日本で全然知られていないような開発手法が存在していて、
完全に時代遅れというわけではなさそうでした。

この件に関して印象深かったのは組込み系の製品開発を行っている会社の方の
「弊社では戦略的にウォーターフォールを採用している」
という話でした。
Webの場合、環境が自分の手元にあるので何か問題が起こった時には修正することができますが、組込みの場合はそうはいきません。最初にMarketing, Products, EngineerごとにRequirements Documentを作成し、中の項目を全てトラッキングしながら開発を進めているということでした。
アジャイル開発というのは別に全てを解決する銀の弾丸というわけではありません。
アジャイルとウォーターフォールは対極にある手法と捉えられがちですが、
両方勉強して自分の環境にとってどのような形が一番良いのかを模索していくべきなのでしょうね。


プログラミング能力の違い


ペアプログラミングなどをしたわけではないので、自身の目で確認することはできませんでした。
日本人エンジニアで15年シリコンバレーで働いている人のお話では、

「シリコンバレーと日本のエンジニアの能力平均は、少なくともコードレベルで言えば同等、
日本の方が高いかもしれない」

ということでした。


使用ツール


JIRAはほぼすべての人が使用していると回答していました。
その他は各社様々で、Review Board, Trello, gerrit, Hipchat, Campfireなどの名前が挙がりました。DevOps的な面でのツールについてはあまり聞けなかったのですが、例えばFacebookではJenkins、ZendeskではChefを使っているということでした。


非エンジニアの能力の違い


・営業

日本のスタートアップの場合、まず日本でそれなりのシェアを獲得し、ある程度の地盤が固まってから世界に売り出していく、いわゆる「グローバル戦略」を考えるという順序になることが多いのではないかと思いますが、シリコンバレーではそもそも「グローバル戦略」という言葉は使われないとのことでした。

なぜならグローバルに売るのが当たり前だから。

シリコンバレーにはグローバルに製品を売っていく経験豊富な人間が数多く集まっていて、さらにエンジニアと同じ場所を拠点に置いているため、製品への迅速なフィードバック・改善が行えるのが強みのようです。


・非エンジニア一般

シリコンバレーのIT企業で働いている人は、専門がエンジニアでなくてもある程度の話が通じるようです。
例として挙げられていたのは、法律関係を専門とする同僚がSHA-1ハッシュを理解していたというもの。
また、シリコンバレーではトップにComputer Scienceをやっていない人間が就くことはありえないと言っていました。
日本の企業ではエンジニアを管理するのは文系出身で技術畑ではないということがしばしばありますが、その場合エンジニアは技術のわからない人に対して説明・説得をするために余分な資料を作らなければいけません。しかもその資料は中間成果物なので、エンジニアの作業時間、資料の管理・修正などコストは膨らむ一方です。
今回は10数人ほどの方とお話していないので、シリコンバレー全体の傾向かどうかはわかりませんが、少なくともお話を聞いた範囲では日本よりもComputer Scienceに対する平均的な理解やリテラシーが高いように感じました。


生活の違い


まず給料についてですが、シリコンバレーでは新卒で約2000万、もしくは1000万+ストックオプションというベースがあるようです (もちろんこれより低い場合はあるのですが、優秀な人を雇おうと思ったらこのぐらい出さないと他社に獲られてしまう)。
この数字だけ見るとため息が出てしまいますが、シリコンバレーはIT企業が集中しているおかげで生活に必要な経費もかなり上昇しているようで、2LDKの月の家賃が約$3000ということでした。
結婚していて子供がいる場合はその分生活費も養育費もかかるし、シリコンバレーは郊外にあるので車を持つことも必須です。
それから解雇される場合、日本では「労働者を解雇する場合30日前に解雇予告をしなければいけない」のですが、シリコンバレーでは解雇通知の日に即日退去というリスクもあります。この辺の事情を考慮すると、給料の額面だけ見て日本と比較するべきではないようですね。

日本のエンジニアがシリコンバレーで働き始めることを考えた場合、上記のお金の面以外に考慮しなければいけない点として人間関係のことが触れられていました。日本からシリコンバレーに引っ越した時、エンジニア本人は会社に来れば同僚がいるのですが、家族は一から人間関係を作っていかなければいけません。
この点はお金以上に家族に負担をかけることになるため、もしシリコンバレーで働きたいのであれば、なるべく独り身の時に来るのがおすすめであるということでした。


まとめ


違いというか、項目ごとに私が感じたことを羅列してみたわけですが、全体の印象として残ったのは

「エンジニアがやっていることは日本とそんなに変わらない、しかしエンジニアを取り巻く環境があらゆる面で異なる」

ということでした。
この「取り巻く環境」というのは一企業の中で閉じるものではなく、シリコンバレー全体で巨大なエコシステムが形成されているもので、恐らく日本で完全に真似ることはできないし、部分的に真似たとしても片手落ちになってしまうでしょう。

私は東北の田舎出身で、東京に初めて来たのは就活をしていた23歳の時でした。
当時は東京に対して過剰なまでの恐れを抱いており、私のような「おしゃれ」にパラメータを割り振ってこなかった人間がひとたび渋谷を歩こうものなら即座に因縁をつけられてボコボコにされるかもしれない、と思っていました。今思えば意味不明ですが、自分の周りと何が違い、何が同じなのかを実際に体験しない限り、未知は未知のままです。
シリコンバレーについても(かなり強引ですが)同様で、シリコンバレーについてのWeb上の記事を見る度に「シリコンバレーという天上界ではこんなことが行われているらしい」「それに比べて自分は・・・」とか無駄に悩んでいたわけです。今回自分の足でシリコンバレーに赴くことで、上記に挙げたような話を聴くことができ、私にとってシリコンバレーは完全に未知の場所ではなくなりました。
開発プロセスの項でも書いたように、大事なのは手段ではなく目的です。シリコンバレーで行われていることを盲目的に取り入れるのではなく、自分にあったものを考えて選ぶということが、重要です。

これだけ書いておいて月並みすぎる結論になってしまいましたが、以上です。



おまけ


結構まじめな感じで書きましたが、私にとっては初めての海外旅行であり、観光面でも大変充実していました。全員でぞろぞろとサンフランシスコの市街に出て行って盛大に迷ったり、タクシーのおじさんにチップを渡しすぎてなぜかお返しにVHSビデオをもらうなど、いろいろありました。

2014年5月21日

ANTLR と Python で構文解析を試してみる

はじめに

Kompira では、ジョブフロー言語の構文解析に、PLY (Python Lex-Yacc) [1] を使用しています。PLY は Yacc/Lex という定番の構文字句解析ツールの Python による実装です。Python から利用できるパーサジェネレータは [2] のように数多くあるのですが、筆者自身が使いなれた Yacc/Lex ベースということと、Python ネイティブな実装ということで、PLY を選んでました。

今回は前から気になっていたパーサジェネレータである ANTLR [3] を使って、簡単な式言語の構文解析を実現してみようかと思います。ANTLR は、LL(*) [4] という強力なトップダウン解析手法にもとづいており、サンフランシスコ大学の Terence Parr 教授が中心となって開発しています。ちなみに Terence Parr 教授は「言語実装パターン―コンパイラ技術によるテキスト処理から言語実装まで 」の著者でもあります。Yacc系の LALR にもとづくツールでは、あいまいな文法を記述すると Shift/Reduce 衝突や Reduce/Reduce 衝突といったエラーメッセージが出ますが、メッセージからはどの構文規則が問題となっているのか直接的にわからないことが多いので、問題解決に苦労するというようなことが良くあります。その点、LL系のツールでは原因となる構文規則を指摘してくれるので、文法のデバッグが楽になるのでは、という期待があります。

トップダウン解析手法によるパーサーですと、何年か前に PEG [5] にもとづくPackrat Parsing が注目を集めていたこともありましたので、そのうち、こちらについても試してみようかと思いますが、今回は ANTLR でいきます。

ANTLR のインストール

まず、我々が普段開発で使用している CentOS 6 のサーバに ANTLR をインストールします。ちなみに、yum コマンドでもインストールできるのですが、バージョンが 2.7.7 と古いので、今回は ANTLR のサイトから必要なパッケージをダウンロードして、手作業でインストールを行います。ただし、最新のバージョン4系はまだ Python に対応していないようですので、今回は1つ前のバージョン
3系を使います。

(0) Java実行環境のインストール

ANTLR は Java で実装されているため、まずはじめに Java の実行環境をインストールします。

(1) パッケージのダウンロード

次に、ANTLR v3 のダウンロードページ (http://www.antlr3.org/download.html) から jar ファイルをダウンロードして適切な場所に配置します。

(2) 起動スクリプトの作成

jar ファイルをラップする起動スクリプトを準備しておくと、あとあと楽なので以下のようなスクリプトを antlr3 というファイル名で作成しておきます。
[antlr3]
実行フラグを立てて、~/binの下に置いておきます。

(3) Python用ランタイムライブラリのインストール

次に、Python用のランタイムライブラリをインストールする必要がありますが、ダウンロードページにあるものはバージョンが少し古く、先ほどダウンロードした jar ファイルと合っていないので、ソースコードからビルドしてインストールします。
以上で、ANTLR の実行環境の準備は完了です。

式言語の定義

以下に示すような BNF で定義される簡単な式言語 explang を ANTLR で構文解析してみることにします。

■ 定義1: explang 文法

 <expression> ::= <assign_expr>
                | <arith_expr>

<assign_expr> ::= IDENT '=' <expression>

 <arith_expr> ::= <term>
                | <arith_expr> '+' <arith_expr>
                | <arith_expr> '-' <arith_expr>

       <term> ::= <factor>
                | <term> '*' <term>
                | <term> '/' <term>

     <factor> ::= IDENT
                | NUMBER
                | '(' <expression> ')'
---

ここで、IDENT は識別子を、NUMBER は非負の整数リテラルを表す終端記号とします。以下のような正規表現で定義されます。

■ 定義2: explang 字句規則

  IDENT = [a-zA-Z_][0-9a-zA-Z_]*
  NUMBER = [0-9]+
---

この式言語では、以下のような式を入力として受け付けることを想定しています。

3 * 2
x = 9 / (3 + 1)
x = y = z = 0
x = (y = 3) + 3

一方、以下のような式はシンタックスエラーとなります。

x + y = 3
(3 x)
- 9

ANTLR 向けに文法を変形する

[定義1: explang 文法] を直接 ANTLR に入力できれば良いのですが、以下に示すように左再帰の規則が含まれているので、そのままでは LL系のパーサージェネレータである ANTLR は受け付けてくれません。(ANTLR v4 では、左再帰の除去を自動的にやってくれるみたいです [6])

<arith_expr> ::= <term>
               | <arith_expr> '+' <arith_expr>
               | <arith_expr> '-' <arith_expr>

そこで、以下のように文法を変形して、左再帰を除去してやります。

<arith_expr> ::= <term>
               | <term> '+' <arith_expr>
               | <term> '-' <arith_expr>

さらに、上の3つの生成規則は、すべて <term> で始まっているため、このままでは、構文解析時にバックトラックが必要になってしまします。そこで、以下のように左端のくくりだしを行います。

<arith_expr> ::= <term> ( | ( '+' | '-' ) <arith_expr> )

これでも良いのですが、ANTLR は拡張BNFが使えるので、0回以上の繰り返しを意味する「*」を用いて以下のようにわかりやすく整理することができます。

<arith_expr> ::= <term> ( ( '+' | '-' ) <term> )*

同様にして、<term> 規則も変形を行います。最終的には以下のように explang 文法を再定義できます。

■ 定義3: ANTLR向け explang 文法

 <expression> ::= <assign_expr>
                | <arith_expr>

<assign_expr> ::= IDENT '=' <expression>

 <arith_expr> ::= <term> ( ( '+' | '-' ) <term> )*

       <term> ::= <factor> ( ( '*' | '/' ) <factor> )*

     <factor> ::= IDENT
                | NUMBER
                | '(' <expression> ')'
---

文法定義ファイルの作成

式言語 explang の文法が定義できたので、これを ANTLR に入力し、Python 実装のパーサーを生成します。以下のように ANTLR に入力する文法定義ファイルを作成します。

[explang.g]
最初の行は文法を宣言している部分で、ここでは、explang という名前を付けています。この名前はファイル名と合わせておく必要があります。

grammar explang;

次の部分はオプションを指定しているところです。デフォルトですと、Java のコードが生成されるので、ここでは、ターゲット言語を Python とするように明示的に指示しています。

options {
    language = Python;
}

次が構文規則を記述している部分です。これは、ANTLR の構文に合わせていますが、内容は [定義3] とほぼ同じです。ただし、ANTLR に開始規則を教えるために、以下のような規則を追加しています。また、空の入力も許すようにしています。

start       : ( expression )? EOF ;

最後は字句規則を記述している部分です。こちらも [定義3] の字句規則の内容を ANTLR の構文に沿って記述しています。NODIGIT と DIGIT の定義は IDENT と NUMBER の定義の中で使われるものなので、トークンとして認識されないように fragment 指定をしています。さらに、空白を読み飛ばすために、以下の定義も追加しています。

WHITESPACE : ( '\t' | ' ' | '\r' | '\n'| '\u000C' )+    { $channel = HIDDEN; } ;

アクションに $channel = HIDDEN; と記述することで、パーサーからはそのトークンが見えなくなり、結果として WHITESPACE がスキップされます。

パーサーの生成と動作確認

パーサーを生成するには、以下のようにコマンドライン引数に文法定義したファイル (explang.g) を指定して、ANTLR を起動してやれば OK です。
うまくいけば、以下の3つのファイルが生成されます。

  • explang.tokens : トークン定義のファイル
  • explangLexer.py : 字句解析ルーチン
  • explangParser.py : 構文解析ルーチン

生成された explangParser.py は簡単な対話型の処理を受け付ける main プログラムが含まれています。そこで、以下のようにオプションを付けて Python から起動すると、プロンプトが表示され、式が入力できるようになります。
そこで、以下のように explang 文法の正しい式を入力すると、何も出力されずに次のプロンプトが表示されます。
間違った式を入力すると、エラーメッセージが表示されます。
終了するには、Ctrl+D を入力します。

おわりに

ということで、ANTLR v3 を使って簡単な式言語のパーサーを作ってみました。今回は、ただ構文解析するだけでしたが、次回は実際に式を評価して結果を表示する簡単なインタプリタを実装していこうかと思います。

参考資料

[1] http://www.dabeaz.com/ply/
[2] https://wiki.python.org/moin/LanguageParsing
[3] http://www.antlr.org/
[4] Terence Parr, Kathleen S. Fisher, "LL(*): The Foundation of the ANTLR Parser Generator", PLDI '11
[5] http://ja.wikipedia.org/wiki/Parsing_Expression_Grammar
[6] https://theantlrguy.atlassian.net/wiki/display/ANTLR4/Left-recursive+rules

2014年3月24日

Kompiraオブジェクトことはじめ

開発部の溝江です。

 Kompiraはジョブフローやそこで使用される各種データ、テンプレートなどをオブジェクトとして管理できる機能を提供しています。
各オブジェクトはブラウザ上から閲覧・編集でき、ジョブフローからもアクセスできます。
デフォルトで用意されているオブジェクト型の他に、自分で型を定義することも可能です。

本記事では上記のようなオブジェクトの使い方について解説します。
(なお、本記事はKompira 1.3.5の仕様に基づき記述しています。)



オブジェクトの作成 

Kompiraをインストール直後、ブラウザからKompiraにrootユーザでログインすると以下の様な画面が表示されます。


KompiraはUNIXライクなファイルシステムを持っており、上記の場合「/root」という位置にあるディレクトリを表示しています。KompiraオブジェクトのパスとURLのパスは対応していて、この場合のURLはhttp://(kompiraサーバ名)/rootとなります。


以下に、デフォルトで用意されている代表的なオブジェクトをいくつかご紹介します。

ディレクトリ


中に複数のオブジェクトを格納できるオブジェクトです。
これにより、ジョブフローや各データをツリー構造上に配置して管理することができます。
画面下部のフォームに名称と型を入力して新規作成ボタンを押すと、ディレクトリに新しいオブジェクトが追加されます。


ジョブフロー


Kompiraの主な機能であるジョブフロー言語を記述するオブジェクトです。ジョブフロー言語の閲覧・編集・実行をすることができます。
ジョブフロー言語の文法についてはKompiraジョブフロー言語基礎文法最速マスターの記事を参照してください。


スクリプトジョブ


シェルやPython, Ruby, Perlなどのスクリプトを記述するオブジェクトです。
ジョブフローから呼び出すことで、Kompiraが動作しているサーバ、またはリモートサーバ上で実行させ、結果を取得することができます。
運用を自動化していくにあたって、既に使用しているスクリプトがある場合、それを全てKompiraのジョブフロー言語で書きなおすのは大変ですし、新たなバグを生み出してしまう可能性もあります。
既存のスクリプトは、スクリプトジョブオブジェクトを利用してフローに組み込むのが良いでしょう。


環境変数


名前と値の組を複数個登録できるオブジェクトです。
最も一般的な使用例は、ジョブフローの中で使用されるパラメータをまとめて環境変数に登録しておくというものです。
前述のジョブフロー画面にもパラメータの入力欄が設けられていますが、ジョブフローを実行する度に変わるようなパラメータはジョブフロー画面で、その他のパラメータは環境変数で管理するといった使い分けをします。


ノード情報/アカウント情報 


ノード情報とアカウント情報は、リモートサーバへの接続に使用する情報を管理できるオブジェクトです。
ノード情報はIPアドレス、接続方式、ポート番号などを、アカウント情報はユーザ名、パスワード、SSH鍵ファイルを登録することができます。
運用フローの中には、1つのサーバについて複数のアカウントを使用する必要がある場合があります。また、多数のサーバを管理している現場では、共通のアカウントを設定しているといったケースが見受けられます。
このようなケースに柔軟に対応するため、アカウント情報とノード情報は別のオブジェクトとして定義されています。

これらの型はジョブフローから使用されることを想定しています。
ジョブフローからリモートサーバに対してコマンドを発行する場合、一番単純に行う方法は、以下のようにIPアドレス、ユーザ名、パスワードを直接指定することです。

[__host__ = '192.168.100.100'] ->
[__user__ = 'username'] ->
[__password__ = 'your_pass'] ->
['ifconfig']

この場合、対象サーバを指定するために3行使って記述をしなければいけません。ポート番号やシェルを指定する場合はさらに記述を増やす必要がありますし、パスワードがジョブフローに埋め込まれてしまうという問題もあります。
より実用的な方法は、アカウント情報とノード情報のオブジェクトを作成し、ジョブフローからそれを呼び出すというやり方です。

[__node__ = /root/test_node] ->
['ifconfig']

この方法であれば、ジョブフローとそれを実行する対象のサーバ情報を切り離すことができますし、ジョブフローを実行する対象サーバを変更する場合も、最初の例より簡単に行えます。


Wikiページ


Creole形式のWikiを記述するオブジェクトです。
ジョブフローやデータに関するマニュアル、注意事項、トラブルシューティングなど、記述したジョブフローを運用していく上で必要な情報を、ジョブフローと同じくKompira上で管理することができます。


ジョブフローからオブジェクトのデータにアクセスする

オブジェクトは内部に複数のフィールドを持っており、ジョブフローからフィールドを指定することで、オブジェクトが持つ値を取得することができます。
例えば「ノード情報」オブジェクトは以下の様なフィールドを持っています。


これらのフィールドには、ジョブフローからobject.displayNameのように、ドット記法でアクセスすることができます。また、フィールド以外にname(オブジェクト名), abspath(絶対パス)など全てのオブジェクトに存在する属性があります。
それでは、オブジェクトのデータにアクセスするジョブフローを書いてみましょう。
ノード情報オブジェクトを/root/test_nodeという場所に作成します。


以下はこのノードの情報を取得・更新するジョブフロー例です。
ジョブフロー中でパスを記述すると、オブジェクトとして解釈されます。

[node = /root/test_node] ->
print(node.ipaddr) ->               # IPアドレスを表示
print(node.conntype) ->             # 接続種別を表示
['14.14.14.14' >> node.ipaddr] ->   # IPアドレスの値を更新
print(node.ipaddr)                  # 書き換えたIPアドレスを表示

実行結果



このように、オブジェクトの情報をジョブフローから呼び出し/編集することができます。

上記例では単一のオブジェクトへのアクセスを行いましたが、複数のオブジェクトの値をまとめて取得したい場合があります。
ディレクトリオブジェクトに対してfor構文を使えば、ディレクトリ内のオブジェクトに順次アクセスすることができます。

[dir = /root] ->
{ for obj in dir |
  print(obj.abspath)    # オブジェクトのパスを表示
}

実行結果

















上記コードの実用例としては、アカウント情報型のオブジェクトのみを持つディレクトリを作成しておき、複数台のサーバに同じフローを順次実行するというパターンがあります。


型オブジェクトの作成

ここまでにご紹介した型はKompiraが標準で用意しているものですが、ユーザが好きな型を定義することもできます。
他のオブジェクトと同じやり方で、「型オブジェクト」という種類のオブジェクトを作成してみましょう。


ここでは、何か障害が発生した際に通知を送るということを想定し、ユーザ情報を入力するためのオペレータ型を定義しました。

オブジェクトの中には複数のフィールドを定義することができ、その種別は以下の中から選択できます。

フィールド種別 説明
String 文字列
Integer 整数値
Boolean 論理値
IPAddress IPv4アドレス形式の文字列のみ入力可能
Email Eメール形式の文字列のみ入力可能
URL URL形式の文字列のみ入力可能
Datetime 日付/時刻
Text 複数行の文字列
LargeText 複数行の文字列 (Textよりさらに広い表示・入力フォームを持つ)
Enum 列挙型
Array 配列
Dictionary 辞書型
Object オブジェクト型 (他のオブジェクトへの参照)
File 添付ファイル型

型オブジェクトを作成すると、オブジェクト作成時の型の選択肢に先ほど作成した型オブジェクトの情報が追加され、オブジェクトを作成することができるようになります。


オブジェクトの編集画面では、フィールド種別に合わせた表示・入力フォームが表示されます。
(以下の画像は、左が閲覧画面、右が編集画面です)


これで先ほど定義したオペレータ型のオブジェクトが作成できました。
自分で定義した型のオブジェクトに対しても、標準の型のオブジェクトと同じようにジョブフローからアクセスすることができます。



まとめ

Kompira上でオブジェクトを作成する機能をご紹介しました。
運用において、自動化したい事柄とそれに絡むデータは多種多様です。
Kompiraではジョブフロー言語とユーザ自身が型を定義できるオブジェクトという仕組みの組み合わせにより、様々な状況に柔軟に対応することができます。


2014年2月26日

KompiraからZabbixのグラフを取得してメール送信してみる

はじめに

先日のOSS運用管理勉強会で Kompira と Zabbix の連携ソリューションを紹介しました(発表資料はこちら)が、そのデモンストレーションの中で障害検出時に「Zabbix のグラフ画像を取得してメールで送信する」というのを行ないました。今回はその部分を簡単にしたものをご紹介したいと思います。

なお Kompira は ver 1.3.4、Zabbix は 2.2.1 を利用しています。

事前調査

Zabbixグラフ画像の取得方法

Zabbixのグラフ画像は例えば以下のような URL で取得できます。

http://example.com/zabbix/chart2.php?graphid=グラフID&period=3600&width=800

ただし、この際に Zabbix のセッションIDを cookie で渡す必要があります。セッションIDは Zabbix にログインしたときに割り当てられる値ですが、今回は Zabbix API を利用してログインしてセッションIDを取得するようにします。

また、URLパラメータの「グラフID」の部分は Zabbix で各グラフに割り当てられている ID を指定する必要があるので、事前に確認しておいてください(確認方法は後述)。

その他のURLパラメータは、以下のとおりです。
  • period : グラフ化する期間(秒数)
  • width : グラフ画像の横幅(ピクセル)
他にも指定できるパラメータがあるようなので、興味がある方は Zabbix の仕様を確認してみてください。

ZabbixグラフIDの確認方法


Zabbix のグラフ表示画面でグラフの上にあるタイトル部分下にある目盛り部分で右クリックして別ウィンドウ(または別タブ)で表示してみてください。先に書いたような URL で、以下のようにグラフ画像だけが表示されているかと思います。URL のパラメータ部分にある graphid= に続く数字が、そのグラフに割り当てられたグラフIDになります。


このグラフの場合 547 がグラフIDとなります。

Zabbix API によるログイン

グラフ画像の取得のためにはセッションIDが必要になりますので、Zabbix API を利用して Zabbix にログインすることでセッションIDを取得します。Zabbix 2.2 の場合は user.login という API を呼び出すことでログインできます。API の詳細については以下を確認してください。


この API を呼び出すには「ユーザ名」と「パスワード」が必要で、呼び出しに成功すると「セッションID」が結果として得られます。

実装

今回作成するオブジェクトは全て /samples/Zabbixグラフ送信 というディレクトリに配置することにします。

Zabbixサーバ情報

ログインに必要な情報である Zabbix サーバの URL、ユーザ名、パスワードを保持する環境変数オブジェクトを「Zabbixサーバ情報」という名前で作成します。また取得したセッションIDも保持できるようにしておきます。

それぞれ url, user, password, sessionid という名称で環境変数に記入しておきます。ただし sessionid はログイン時にジョブフローから書き込みますので、この段階では空欄にしておきます。

作成した「Zabbixサーバ情報」オブジェクトを以下に示します。


ZabbixAPI呼び出し

Zabbix へのログインを行なうために、Zabbix API を呼び出すジョブフローを「ZabbixAPI呼び出し」という名前で作成します。なお、このジョブフローはログイン以外の Zabbix API も呼び出せるように少し汎用的に作っておくことにします。


ポイントとなる箇所を簡単に説明します。

  • 8~14行目、Zabbix API の呼び出しに必要なパラメータを準備しています。パラメータの基本は API の種類を示す method と、API の種類毎に決められた形式の params、および認証のためにセッションIDを渡す auth です。API 呼び出し結果を区別するための id は、今回は利用しないため 1 固定とします。jsonrpc'2.0' と指定することが Zabbix API で決められているため固定とします。
  • 15行目、urlopen() 組み込みジョブを利用して Zabbix API を呼び出しています。上で準備したパラメータを JSON 形式で渡すために、引数 encode='json' を指定しています。
  • 23行目、Zabbix API を呼び出した結果が JSON 形式で帰って来ていますので、json_parse() 組み込み関数を利用してジョブフローで利用できる辞書形式に変換しています。
  • 24~33行目、呼び出した API が user.login の場合はセッションIDが result として得られるので、「Zabbixサーバ情報」環境変数の sessionid に書き込んでいます。user.logout を呼び出した場合は環境変数の sessionid をクリアしています。
  • 34行目、Zabbix API の呼び出し結果で得られた result 部分をジョブフローの戻り値として return しています。


Zabbixグラフ取得

得られたセッションIDを利用して Zabbix からグラフ画像を取得するジョブフローを「Zabbixグラフ取得」という名前で作成します。このジョブフローは「グラフID」を指定すると、それに対応するグラフ画像データを返すように作ります。



ポイントとなる箇所を簡単に説明します。

  • 1行目は、取得するグラフIDを指定するパラメータ graphid を定義しています
  • 2~3行目、グラフ化する期間やグラフ画像の幅もパラメータとしていますが、デフォルト値を定義しておくことでパラメータを省略してこのジョブフローを呼び出せるようにしておきます。
  • 10行目で、(次節のジョブフローで)取得済みのセッションIDを zbx_sessionid という名前の cookie で渡すための準備をしています。
  • 11~12行目、グラフIDおよびその他パラメータを含んだ URL を準備しています。
  • 13行目、urlopen() 組み込みジョブを利用して、グラフ画像の取得を行っています。辞書型データを cookies という引数で渡すことで、URL アクセス時の cookie を指定しています。
  • 20行目、正常にアクセスできた時は、レスポンス本文が画像データなのでジョブフローの戻り値として呼び出し元に返す。
  • 22,25行目、何らかのエラーが発生した場合は status=1 として異常ステータスを呼び出し元に返します。


Zabbixグラフ送信

上で準備したジョブフローを組合せて、Zabbix にログインおよび画像取得を行ってメールで送信するジョブフローを「Zabbixグラフ送信」という名前で作成します。このジョブフローでは取得するグラフを複数指定できること、および、メールの送信先や表題なども指定できるように作ります。


ポイントとなる箇所を簡単に説明します。
  • 1行目は、グラフIDを指定するパラメータ graphids を定義しています。複数のグラフを指定できるように配列形式で渡されることを期待しています。
  • 2~5行目、メール宛先などのメール送信に関連するパラメータを定義しています。いくつかのパラメータは簡単のためにデフォルト値を指定しています。
  • 11行目で、「ZabbixAPI呼び出し」ジョブフローで user.login を呼び出して、セッションIDを取得しています。パラメータのユーザ名とパスワードには環境変数に設定したものが渡され、結果得られたセッションIDは環境変数に記録されます。
  • 18行目、得られた複数の画像データを格納する空の配列変数 files を用意しておきます。
  • 19~25行目、グラフID毎に「Zabbixグラフ取得」ジョブフローを呼び出して、グラフ画像のデータを取得しています。取得できた場合は namedata という要素を持つ辞書形式(これがひとつのファイルに相当する)にして、それを配列 files に追加していきます。ファイル名(name)は Zabbix から得られないので、グラフID + ".png" としています。
  • 33行目、グラフ画像が1枚以上得られたときは、それらを添付ファイルとしたメールを to 宛に送信しています。mailto() 組み込みジョブの attach_files 引数には添付ファイルフィールドや、name, data 要素を持つ辞書を指定できます。またそれらを配列に入れて渡すことで複数のファイルを添付することができますので、その仕組みを利用しています。
  • 37行目、グラフ画像が得られなかったときは、メールを送信しません。
  • 43行目、Zabbix からログアウトするために、user.logout を呼び出しています。

実装オブジェクト一覧

以上で実装が完了しました。実装したオブジェクトの一覧は以下のようになっています。



動作確認

では実際にジョブフローを動かしてみましょう。

Zabbixグラフ送信の実行

ここでは3つのグラフ画像(グラフIDは 524, 525, 547)を取得して、自分宛てにメールを送信させたいと思います。「Zabbixグラフ送信」画面で、パラメータ欄 graphidsto を以下のように記入して「▶実行」ボタンを押して、ジョブフローを実行します。



パラメータ記入のポイント:

  • graphids は配列形式にするため [] で囲って複数のグラフIDを記入します。
  • to は文字列で指定するため ' または " で囲って記入します。


ジョブフロー実行結果

実行したジョブフローが問題なく完了すると、プロセス詳細画面のコンソールが以下のように表示されているはずです。


Zabbix にログインして、3枚のグラフ画像を取得してメールを送信し、最後にログアウトしている様子が分かります。

ちなみに手元の環境(Kompira と Zabbix を同時に動かしています)でこのジョブフローの実行時間は約9秒でした。

受信メール確認

最後に、今回作成したジョブフローが Zabbix からグラフを取得してメールを送信できているか確認してみると、ちゃんと以下のようなメールが届いていました。



スクリーンショットには入りきりませんでしたが、3枚目のグラフ画像も添付されています。

まとめ

今回は Kompira で Zabbix からグラフ画像を取得するために、urlopen() 組み込みジョブを利用してみました。また mailto() 組み込みジョブに取得した画像データを渡すことで、添付ファイルとしてメールを送ることができることを示しました。

また、セッション ID を取得するために、Zabbix API を呼び出すジョブフローも実装してみました。お気付きの通り、ログインやログアウト以外の API にも利用できますので、これをベースにして Kompira と Zabbix の様々な連携の可能性も広がるかと思います。

より実践に近いデモンストレーションでは、Zabbix が障害を検出した時に Kompira からサーバに自動的にログインしてプロセス状態やログを収集したうえで、HTML メールにそれらの情報と共に(今回紹介した仕組みで取得した)グラフ画像をインラインに貼り付けて送信し、さらにクラウド電話サービスで障害発生の通知まで行っています。

2014年1月17日

第55回プログラミング・シンポジウム参加報告


はじめに


先日の1月10日(金)~12日(日)の3日間、伊豆の伊東温泉で情報処理学会の第55回プログラミング・シンポジウム(通称プロシン、http://www.ipsj.or.jp/prosym/55/55program.html)が開催されました。実は昨年のプロシンで Kompira について発表(※)したのですが、その発表内容が案外に良かったらしく、山内奨励賞というのをいただけることになり、その表彰式と受賞講演のために、今年も参加してきました。

プロシンは、毎年この時期に伊豆や箱根など東京近郊の温泉地で開催され、プログラミングに関連した話であれば何でもOKという自由な雰囲気のシンポジウムです。参加者のバックグラウンドは大学や研究機関、企業などの研究者やソフトウェア開発者、また年齢も20代から80代?までと幅広く、この業界のコミュニティの新年会的な感じで(特に夜の部は)盛り上がります。ちなみに夏のプロシンというのもあり、こちらは最近では都内で1日開催されているので、参加されたり耳にした方もいるかもしれません。

さて、私は初日の午前中は客先での打ち合わせがあったため、伊東に到着したのは午後4時半くらいと表彰式の直前となってしまいました。以下では私が聴講したものをいくつかピックアップして簡単に紹介していきたいと思います。

1日目


京都大学の馬谷先生の「JVMバイトコードへの低水準操作を簡潔に記述可能なマクロシステム」では、JVMバイトコードに対する書き換え操作をbc-macroという独自のマクロ展開ツールを用いて行う手法について発表していました。一般的にはJVMのバイトコードをいじる場合、ASMで低水準な操作として記述するかAspectJなどのアスペクト指向プログラミングを用いてバイトコードの特定の個所にコードを織り込んでいくようですが、前者はどうしても記述が煩雑になる一方、後者では柔軟性が乏しいという課題があります。馬谷さんは今では絶滅危惧種の(と会場では呼ばれてました)Lisperらしくマクロを用いてJVMの木構造を書き換えるツールであるbc-macroを開発したとのことです。ちなみにbc-macroはClosure言語のライブラリとして実装されているそうです。発表では、bc-macroを用いて末尾再帰の最適化を行う例を示していました。

2日目


琉球大学の河野先生による「AgdaでのProgramming技術」という発表では、Haskellのモナドをきちんと理解するために、Agdaを用いて圏論の性質を証明しようという試みでした。ちょっとAgdaは強力な型システムを持ったHaskellライクなプログラミング言語とみなすこともでき、型を証明したい命題とし、その型を満たすラムダ式のプログラムを証明とするカリー・ハワード同型対応にもとづく証明支援系です。この発表を聞いて私もAgdaで遊んでみたくなりました。


慶応大学の服部先生による「変数名を用いないプログラミングの試み」は、変数名を用いないでどこまでプログラミングが可能か?ということを追求した発表でした。プログラムを作成する際、変数名を考え出すのに結構時間がかかってしまうことは、プログラマの皆さんなら同意してくれるかと思いますが、ならばいっそのこと変数名無しでプログラムを作成してみたらどうなるか、という話です。関数型言語界隈の人たちはここで、コンビネータをつかったプログラミングのことか?と想像するでしょうが、さにあらず、ここではC言語風の手続き型言語を用いて例によるプログラミングという手法を使っていました。例によるプログラミングというのは、Excelのマクロ記録みたいなやつで、実際の操作を与えるとプログラムを作成してくれるというやつです。ここでは、変数を一切用いずにかわりに具体的な数値や文字列と記号のみでプログラムを記述し、そこから、普通の変数名の埋め込まれたプログラムを推測することをやっていました。さすがに例として示されたプログラムはかなり長くなったり、かえって読み難くなったりして、実用性には乏しいかと思いますが、試みとしては面白かったと思います。

2日目の昼には、招待講演として明治大学の杉原先生が「視覚の数理モデルと錯視図形の設計法」というタイトルで登壇されました。「不可能モーション」という動画がYouTubeにアップされ(http://www.youtube.com/watch?v=hAXm0dIuyug)話題となったり、日本テレビ「世界一受けたい授業」に出演されたりしてますのでご存じの方も多いかと思います。この講演では、人間の錯覚を利用して、一般には不可能な動きや、エッシャーのだまし絵に出てくるような形の物体を紹介していました。杉原先生は、平面に書かれた立体図をコンピュータに理解させる研究をなさっていたようなのですが、ある時、エッシャーのだまし絵のような不可能な平面図をコンピュータに入力したところ、コンピュータが可能な3次元の解を出力したために、このような可能性に気がついたとのことです。

2日目の夕方には、別の山内奨励賞受賞者である電通大の中野先生による受賞講演がありました。中野先生は、昨年のプロシンで「ジグソーパズルによる関数型プログラミング」という発表をされたのですが、今回の受賞講演では、ジグゾーパズルプログラミングの発想が誕生するまでの経緯についての話でした。そもそもジグゾーパズルプログラミングでは、ジグゾーパズルのようなピースをうまくはめ込んでいくことで、3進2進変換のような計算処理をジグゾーパズルで実現できるという大変ユニークな内容です。詳しくは論文(https://ipsj.ixsq.nii.ac.jp/ej/?action=pages_view_main&active_action=repository_view_main_item_detail&item_id=95547&item_no=1&page_id=13&block_id=8)を見ていただくとして、今回の受賞講演では、この発想がコラッツ問題に取り組んでいる際に気がついたという話を紹介していました。

3日目


電通大の鈴木さんによる「無音動画に対する効果音貼付けシステムの開発」では、効果音を自然な形で引きのばしたりして尺を調整し、音のついていない動画に張り付ける手法を説明していました。爆発音や破裂音の場合、そのまま繰り返して長さを調整することはできないため、この手法では、元となる音と、ピーク部分を除去した波形を重ね合わせることで自然に聞こえるようにしていました。

東京理科大の酒井さんの「コンパイラ共通基盤COINSのLLVM向け拡張」という発表では、コンパイラ共通基盤 COINS の中間表現を LLVM の中間表現に変換する手法について説明していました。これによって、COINSで実現されている様々な最適化手法とLLVMが持っている様々なバックエンド対応のいいとこどりができるようです。発表では、いくつかのベンチマーク結果も紹介していました。

おわりに


以上、ご紹介したように、プログラミング・シンポジウムでは、発表のテーマがかなり多岐にわたっており、普段あまり触れることのない分野についても最新の研究成果や研究の動向を知ることができる貴重な場かと思います。また機会があれば、参加してみたいと思います。

---
※ 発表スライドと論文はこちらにあります(2014年1月17日追記)