2009/12/30

Anakia 1.0: 出力フォーマットのTips

Anakiaに限らずVelocityを使うと、テンプレートと出力されたファイルの間に予期しない改行コードが埋め込まれるなどの違いが生まれます。 今回はVelocityの MLに投稿された記事を元に対策をメモしておきます。

問題の現象について

MLなどでも度々問題になっているようですが、Velocityでは命令の前に埋め込まれた空白などがそのまま出力されてしまいます。

例えばanakiaを使っているとして、次のようなテンプレートを準備します。

vslテンプレート

<?xml version="1.0" encoding="UTF-8" ?>
<html>
#document()
</html>
#macro (document)
  #set ($allSections = $root.getChildren())
  #foreach ( $section in $allSections )##
    #if ($section.getName().equals("p"))##
    <p>
      $section.getContent()
    </p>
    #end
  #end
#end

出力は次のようになります。

入力ファイル

<?xml version="1.0" ?>

<blog>
  <p>sample messages</p>
</blog>

vslテンプレートを適用した出力例

<?xml version="1.0" encoding="UTF-8" ?>
<html>
            <p>
        sample messages
      </p>
      </html>

期待する出力は、pの開始タグと閉じタグの先頭が縦に揃っていて、内部にコンテンツが少し字下げされて表示されているものなのですが、少し違います。

期待する出力

<?xml version="1.0" encoding="UTF-8" ?>
<html>
      <p>
        sample messages
      </p>
</html>

VTL命令行にある空白の出力抑制

Velocityのテンプレート言語(VTL)の命令はスクリプト言語なので、 書く時には適当にインデントを追加しないと人間には読みずらくなります。

しかし、そのインデントに使われた空白がそのまま出力されてしまうと意図しない空白となる場合があります。

修正版のvslテンプレート

出力をみながらテンプレートを修正します。

修正版 vslテンプレート

<?xml version="1.0" encoding="UTF-8" ?>
<html>
#document()
</html>
#macro (document)
#set ($allSections = $root.getChildren())
#foreach ( $section in $allSections )
#if ($section.getName().equals("p"))
      <p>
        $section.getContent()
      </p>
#end
#end
#end

出力例

<?xml version="1.0" encoding="UTF-8" ?>
<html>
      <p>
        sample messages
      </p>
</html>

出力は意図したようになりましたが、 書かれたテンプレートはかなり人間には読めないものになってしまいました。

vslファイルの自動生成

人間に読めなくて大変なら、人間が編集するファイルとanakiaが使うファイルを分ければ良いという発送で、'#'で始まる行は先頭の空白を取り除くようにしようと思います。

build.xmlは次のようになっていて、 prop/blog.vsl ファイルを自動生成します。

build.xmlファイルから抜粋

<anakia basedir="${docs.src}" 
   destdir="${docs.dest}"
   extension=".html"
   style="prop/blog.vsl"

簡単にできるのでMakefileを使う事にしました。

Makefileの内容

all: prop/blog.vsl build

prop/blog.vsl: prop/blog.vsl.template
	sed -e 's/^ *#/#/' $< > $@

build:
	ant -f prop/build.xml

blog.vsl.template はインデントを使って編集して、makeコマンドで blog.vsl を生成しています。 Makefileに限定する必要もないのですが、 blog.vslblog.vsl.template より新しい場合には生成過程がスキップされるので無駄が省く事ができます。 大した時間じゃないですけどね。

改行コードの出力を抑制する

さて今回使ったのは簡単な例ですが、場合によっては本来1行に収めたいものを複数行に渡って書きたい場合があるかもしれません。 その場合には"##"を行末に入れる事で、改行の出力を抑制する事ができます。

vslテンプレート

<?xml version="1.0" encoding="UTF-8" ?>
<html>
#document()
</html>
#macro (document)
  #set ($allSections = $root.getChildren())
  #foreach ( $section in $allSections )
    #if ($section.getName().equals("p"))
      <p>##
        $section.getContent()##
      </p>
    #end
  #end
#end

改行を抑制した例

<?xml version="1.0" encoding="UTF-8" ?>
<html>
      <p>        sample messages      </p>
</html>

これはp開始タグと getContent() が出力された後の改行が抑制されています。 sample messages前後の空白を抑制したい場合には次の2つのパターンが考えられると思います。

例1

      <p>$section.getContent()</p>

例2

      <p>##
$section.getContent()##
</p>

まとめ

今回は2つのTipsを書きましたが、Velocityを使うと出力されるHTMLコードが汚なくなると感じた場合には有効だと思います。

これぐらいならVelocityのTutorialにあっても良さそうなものですが、 知らないとどうしようもないですよね…。 出力に気を配る人はそんなにいないのかなぁ。

alixをブロードバンドルーターにする

いままで使っていたCoregaのブロードバンドルーターに問題はなかったのですが、 時々動作が不安定になる事があって自分でBフレッツと自宅LANとのゲートウェイを作ってみる事にしました。

目的は、とりあえずalixをブロードバンドルーター代りにしてみることと、ポートスキャンの様子を観察する事です。

参考資料

今回は次の資料を主に参考にしました。 特に作成したiptablesの設定については、参考資料1,2の2つを合わせてベースとして使わせて頂いています。

最終的なiptablesのリファレンスとしては参考資料3を参考にしました。

全体の構成

使ったalixはalix2c3で、3つのethernet portを持っています。実験ネットワーク(192.168.10.0/24)は無線LANのgatewayを置く事を考えていて、内部からInternetへのアクセスを許可して、192.168.1.0/24には許可しない構成を考えています。

  • eth0(ppp0): global IP address (BフレッツとのPPoE接続用)
  • eth1: 192.168.1.1 (192.168.1.0/24 主ネットワーク用)
  • eth2: 192.168.10.1 (192.168.10.0/24 実験ネットワーク用)

将来的には別のalixを使って新たにネットワークを作りOSPFの実験なども行ないたいと思っています。

フィルターポリシー

eth1,eth2の間は特にルーティングを制限する事はいまのところ考えていませんが、将来的には制限を加える可能性があります。 またeth0については参考にさせて頂いた設定例を参考に、ppp0を通るパケットについて制限を加える事にしました。

具体的な制限の内容は、Internetからの入力については基本的に不許可として、弾力的にssh, bittorrentなどの通信を許可する事ができる仕組みを準備する事としました。 その他に通常使っているpingやtracerouteといったコマンドは外部から拒否しつつも、内部からは使えるようにする事としています。

iptablesの使い方

iptablesコマンドをそのまま使うというケースはほとんどないと思います。 CentOSなどRHEL系列では/etc/sysconfig/iptablesファイルにその内容を記録しますし、Ubuntuではufwパッケージを使い、/etc/ufw/*.rulesファイルを使う事ができます。

今回はdebianを使っていますが、パッケージを追加する他に適当な方法がありません。 そこで自前のスクリプトを/usr/local/sbin/iptables.shに準備する事にしました。

このスクリプトは起動時ではなく、PPPoEが実行された後で実行する必要があります。 そこで/etc/network/interfacesファイルを使う事にしました。 PPPoEを使うための設定は次のようになっています。

...
auto dsl-provider
iface dsl-provider inet ppp
  pre-up /sbin/ifconfig eth0 up # line maintained by pppoeconf
  provider dsl-provider
  post-up /usr/local/sbin/config_iptables.sh

auto eth0
iface eth0 inet manual
  mtu 1454

このpost-upを使い、ppp0インタフェースが起動した後に必ずiptablesの設定が行なわれるようにしています。 interfacesファイルを使った設定については、$ man 5 interfacesを参考にしてください。

Debian Etchのインストール

以前投稿した記事(Ubuntu 7.10からALIXでブート可能なCFカードを作成する)を参考にしてください。 ここではalixでdebianとsshdが起動している事を前提に進めています。

PPPoEの設定

参考にしたサイトでは手動で設定を入れていますが、pppoeconfパッケージが面倒をみてくれます。

$ sudo apt-get install pppoeconf
$ sudo /usr/sbin/pppoeconf

質問への答えで困るところは特にないはずです。 プロバイダから貰った接続用のID, パスワードを設定すれば基本的には終りです。

PPPoE設定の変更

設定ファイル/etc/ppp/peers/dsl-providerを変更しました。

$ sudo diff -u /etc/ppp/peers/dsl-provider.20091223 /etc/ppp/peers/dsl-provider
38c38
< usepeerdns
---
> #usepeerdns
66c66
< mtu 1492
---
> mtu 1454

usepeerdnsをコメントアウトしたのは、自前のDNSサーバーを持っているからです。 そうでなければ変更する理由はないと思います。

mtuを1454に変更したのは、Bフレッツの設定例に載っているからです。 これに併せて家のネットワークにあるLAN内の機器についても上記eth0と同様にmtu 1454を設定しています。

接続テスト

ponコマンドを使って、ppp0インタフェースが構成されるか確認しましょう。

$ sudo pon dsl-provider
$ /sbin/ifconfig ppp0

テストが終ったら接続を切ります。これを忘れるとppp0, ppp1, ...とインタフェースが増えてしまいます。

$ sudo poff -a
起動時にPPPoEを立ち上げる

電源ONと同時にBフレッツに接続するために、/etc/network/interfacesiptablesの使い方にあるように変更します。

不具合があった場合の対応方法

もしプロバイダから送られてきたIDやパスワードに変更があった場合には、$ sudo pppoeconfを再度実行して必要な変更を加えた方が早いと思います。

そういった場合も含めて、自分で設定を書き換えたい場合には/etc/ppp以下にあるファイルにgrepをかけて必要な情報全体を修正してください。

ID, パスワードの情報はpap-secrets, chap-secretsファイルの他にdsl-providerファイルにも含まれています。

ブロードバンドルーターとしての設定

いまのところalixからInternetへは繋っていますが、LANで繋っている他のPCからはInternetへの通信ができないままです。

つまりeth1のネットワーク(192.168.1.0/24)にあるPCからの通信は、 eth0->ppp0を通ってInternetに出ていかないように設定されています。 そのためppp0,eth1,eth2の各インタフェース間を行き来できるように設定を加えていきます。

sysctl設定

Ubuntuを含むDebianの系統では、systcl関連の設定を/etc/sysctl.confで行ないます。 最低限必要な設定はnet.ipv4.ip_forward=1だけですが、sysctl.confファイル全体の設定は次のようにしました。

$ egrep -v ^# /etc/sysctl.conf | egrep -v ^$
net.ipv4.conf.default.rp_filter=1
net.ipv4.conf.all.rp_filter=1
net.ipv4.tcp_syncookies=1
net.ipv4.ip_forward=1
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.secure_redirects = 1
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.all.accept_source_route = 0
最低限のiptables設定

通常はiptablesのFORWARDポリシーはACCEPTになっているため、一つしかないグローバルIPアドレスを共有するためのNAT(IP MASQUERADE)設定を追加するだけで動くようになります。 この段階での/usr/local/sbin/iptables.shスクリプトの内容は次のようになります。

#!/bin/bash
umask 066
PATH=/sbin:/usr/sbin:/bin:/usr/bin
export PATH
iptables -F
iptables -t nat -F
iptables -Z
iptables -X
iptables -t nat -X
iptables -t nat -A POSTROUTING -o ppp+ -j MASQUERADE

2010/01/04追記:
このままではスクリプトを複数回実行した時に、ターゲット(-t)に指定したNAT関連の設定が初期化されずに残ってしまいます。 そのため次の2行を追記しました。 iptables -t nat -Fiptables -t nat -X

もちろん、このままでは危なくてInternetに常時接続する事はできません。 最低限確認しておく事はまだあります。

セキュリティ其の一:LISTENポートの確認

Internetのどこかにいる人が、このalixにアクセスする事が可能か確認する事にします。 今回はSheldsUp!ポートスキャンサービスを利用してみます。

リンク先に進むとShieldsUp!が検出したグローバルIPアドレスが表示されます。 Proceedボタンを押すと、サービスの選択画面が表示されます。 All Service Portsを選択して1056ポートをチェックします。

何も設定していないとOpenSSHの22番ポートが赤いOpenにチェックされているのではないでしょうか。 またpingに反応する設定にしていても問題だと認識されます。 外部からICMPパケットを送り込んで内部の状況を観察する方法もあるので、 Firewallの設定は適切に行なう必要があります。

セキュリティ其の二:LISTENポートのクローズ

今やInternetと家のLANとのゲートウェイになったalixが、どのようなネットワークサービスを提供しているのか確認しておく事にします。

$ netstat -na --protocol=inet
稼働中のインターネット接続 (サーバと確立)
Proto 受信-Q 送信-Q 内部アドレス            外部アドレス            状態       
tcp        0      0 0.0.0.0:111             0.0.0.0:*               LISTEN     
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN     
tcp        0      0 127.0.0.1:6010          0.0.0.0:*               LISTEN     
tcp        0      0 192.168.1.1:22          192.168.1.3:50845       ESTABLISHED
udp        0      0 192.168.1.1:34761       192.168.1.2:123         ESTABLISHED
udp        0      0 192.168.1.1:111         0.0.0.0:*

ここで問題になるのは、状態がLISTENであるもののうち、内部アドレスが0.0.0.0:22になっているSSHのサービスです。 :::22あるいは0.0.0.0:22となっている場合、eth0, eth1, eth2, ppp0のいずれのインタフェースからの接続も許可してしまいます。 そのためポートスキャンサービスでは、Openと検出され警告の対象となります。

Internetからのssh接続は必要ないため、アクセスを制限する事にします。

SSHのポート制限

SSHデーモン(sshd)の制限は、Debianを始め主要なDistributionでは/etc/ssh/sshd_configを通して行なわれます。 変更個所は以下の通りです。

** 変更前 **

ListenAddress 0.0.0.0

** 変更後 **

ListenAddress 192.168.1.1
ListenAddress 192.168.1.1

この変更により、192.168.1.1, 192.168.10.1を宛先とする接続を許可する事ができます。 つまり、クライアントはIPアドレスが192.168.1.0/24, 192.168.10.0/24のいずれかに限定できる事になります。

portmap関連のセキュリティ

"0.0.0.0:111"のように表示されれば、それはportmapが稼働している事を意味しています。 今回はautofsパッケージを導入したため、portmapも稼働する事になっています。

しかしalix以外からの接続を許可する必要はないため、"127.0.0.1:111"と表示されるように変更をします。 まずportmap自身は-i, -lの2つのオプションで、SSHのListenAddressと似たような設定をする事が可能です。 これはportmapの起動時の引数で設定するため、/etc/default/portmapファイルを編集します。

OPTIONS="-l"

しかしportmapをSSHのように192.168.1.1, 192.168.10.1の2つのアドレスで待ち受けるようにする事はできないようです。 また、Linuxに付属するportmap関連のdaemonの中には、上記のようにListenAddressを変更する事ができないものもあります。

そこで、rpc.statdなどの対策のために今回は/etc/hosts.allow, /etc/hosts.denyファイルも編集する事にします。

hosts.allow, hosts.denyの編集

tcpwrapperと呼ばれるライブラリを使うように作成されたプログラムでは、一般的に /etc/hosts.allow, /etc/hosts.denyという2つのファイルを使用してネットワーク接続を制限する事ができるようになります。

さて、どのプログラムがtcpwrapperを使う事ができるのかはリンクされているライブラリから伺い知る事ができます。

$ ldd /usr/sbin/sshd
	linux-gate.so.1 =>  (0xb7f75000)
	 => /lib/libwrap.so.0 (0xb7f68000)
	libpam.so.0 => /lib/libpam.so.0 (0xb7f5d000)

libwrap.so.0が表示されれば、tcpwrapperの機能を含んでいると考えられます。 実際に/sbin/portmapにlddコマンドで確認しても、libwrap.so.0が含まれています。

今回はとにかく家のLANからのアクセスだけを許可したいので、 細かい制御は止めて一括でルールを設定します。

/etc/hosts.allow

ALL: 192.168.1.0/24 192.168.10.0/24

/etc/hosts.deny

ALL: ALL
閑話休題:Linuxでのネットワークアクセス制御

Linuxでは各プログラム独自のアドレス、ポート管理の他に、tcpwarpperというライブラリによるプログラムの制御、iptablesによるカーネルレベルでの管理を行なう事ができます。 低レベルなところで動くiptablesで十分かもしれませんが、iptablesはとびらにつける鍵のようなものです。 LISTENするアドレスを制限する事で、外に向けてとびら自体を作らないという考え方がより堅牢とはいえます。

セキュリティ其の三:iptables

これまでの方法で外部から直接的にシステムに侵入する事は難しくなっています。 しかしまだpingに反応しますし、まだ攻撃対象となる可能性があります。

/etc/network/interfacesファイルから呼ばれる/usr/local/sbin/iptables.shスクリプトの構成について説明します。

この文書の最低限のiptables設定で作成したスクリプトでは、 ざっとみて下記に挙げたようないくつかの観点が抜けています。 いきなり完璧なルールを作る事はできませんが、将来的に拡張可能な小さいルールセットを作成して、 徐々に拡張して行くのが良いと思います。

  • デフォルトポリシーの設定
  • ログの取得による稼働状況の確認
  • インタフェース毎の制御
  • 外部設定ファイルによるinbound接続の許可
  • 不正なIP送信元、送信先パケットの拒否
  • 不正なTCPフラグを持つパケットの拒否

まずは全体的な構成に関わるログの取得のところまでを先に説明します。 各論はまた別のテーマにしたいと思います。

デフォルトポリシーの設定

前にも書きましたが、何のiptablesの設定を行なわないとACCEPTがデフォルトのポリシーになっています。

$ sudo /sbin/iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination         
Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         
Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination  

このままではフィルタールールを設定しても、そこから漏れたものがalixを通過していってしまいます。 そこでiptables -Fなどで初期化した後に、デフォルトルールを設定します。

iptables.sh

...
iptables -F
iptables -Z
iptables -X

iptables -P INPUT DROP
iptables -P OUTPUT DROP
iptables -P FORWARD DROP

iptables -t nat -A POSTROUTING -o ppp+ -j MASQUERADE
...

ポリシーが適用できるのは、この3つのINPUT, OUTPUT, FORWARDだけです。

ログの取得による稼働状況の確認

とりあえずログをとりつつ、許可を与えるルール(logaccept)と、不許可にするルール(logdrop)を準備する事にします。

iptables.sh: ログ取得用コード

...
iptables -P FORWARD DROP

iptables -N logdrop
iptables -A logdrop -m limit --limit 5/minute -j LOG --log-level warn --log-prefix "myfilter drop "
iptables -A logdrop -j DROP

iptables -N logaccept
iptables -A logaccept -m limit --limit 5/minute -j LOG --log-level debug --log-prefix "myfilter accept "
iptables -A logaccept -j ACCEPT
...
インタフェース毎の制御

ここから先はiptablesがどのようにルールを適用させていくか理解しておかないと面倒になってきます。 参考資料3の中にある図で確認するのが良いでしょう。 この図は家からInternetに出ていく方向(outbound)か、Internetから家のLANに入ってくる方向(inbound)かによって、少し頭を切り替えて上部の"Packet in"のところから流れを追い掛ける必要があります。

既に設定されているMASQUERADEによるIPアドレスの変換は、outboundに対するPOSTROUTINGのタイミングで行なわれています。その手前で行なうfilter TableでのFORWARD Chainでの制御は家のLAN内部で通用するIPアドレスを使って制御する事ができます。

逆にinboundでの外部からLAN内部の機器に対するIPアドレス変換を行なう場合は、PREROUTINGのタイミングで設定すると、指定するIPアドレスがLAN内部のアドレスで良くなります。

またこの図からalixでのINPUT, OUTPUT Chainの設定は、ailx自身からの接続に対してだけ適用される事もわかります。単純にalixを通過するだけの通信は、inbound, outbound共にFORWARD Chainが適用される事になります。

alixを通過する通信もalix自身の通信も、個別に制御する必要性は低いと思います。 そのため3つを制御するのではなく、inboundとoutboundで制御する方法を選択する事にしました。

ここではppp0, ppp1などのインタフェース(ppp+)を通過するパケットを、inbound, outboundそれぞれinppp, outpppという名前で制御する事にします。

iptables.sh 続き2

...
iptables -A logaccept -j ACCEPT

iptables -N inppp
iptables -N outppp

iptables -A INPUT   -i ppp+ -j inppp
iptables -A FORWARD -i ppp+ -j inppp
iptables -A OUTPUT  -o ppp+ -j outppp
iptables -A FORWARD -o ppp+ -j outppp
...

eth1, eth2を通過するパケットも同じような名前をつける事で制御が可能ですが、 とりあえず現状では制御はしない事にします。

iptables.sh: 最後でeth1, eth2を許可するルール

...
iptables -A inppp -j logdrop
iptables -A outppp -j logdrop

iptables -A INPUT   -i eth1 -j ACCEPT
iptables -A OUTPUT  -o eth1 -j ACCEPT
iptables -A FORWARD -i eth1 -j ACCEPT

iptables -A INPUT   -i eth2 -j ACCEPT
iptables -A OUTPUT  -o eth2 -j ACCEPT
iptables -A FORWARD -i eth2 -j ACCEPT

FORWARD Chainに-o eth1-o eth2を追加していないのは、Internetを含めた外部からの通信がeth1やeth2を通る可能性があるからで、そのようなパケットはinpppoutpppで許可し、漏れたものは拒否するようにしたかったからです。

外部設定ファイルによるinbound接続の許可

さて少し順序が逆になりますが、外部へwebサーバーを公開するといった手段のために、外部の設定ファイルを使ってinboundの接続を許可しようと思います。

とりあえず外部からデスクトップPC(192.168.1.10)の22番(ssh)ポートへのアクセスを許可するための設定は次のようになります。

参考:外部からのinbound ssh接続を許可するコード

iptables -t nat -A PREROUTING -p tcp -i ppp+ --dport 22 -j DNAT --to-destination 192.168.1.10
iptables -A inppp -p tcp --dport 22 -d 192.168.1.10 -j ACCEPT

PREROUTINGでとりあえず192.168.1.1から192.168.1.10への接続のように変換し、その上で改めて192.168.1.10への接続の許可を与えています。

これを踏まえて、/usr/local/etc/iptables.inboundに次のようなルールを記述しました。

/usr/local/etc/iptables.inbound

tcp,22,192.168.1.210

iptables.sh 設定ファイルを読み込む処理

...
iptables -A FORWARD -o ppp+ -j outppp

OIFS=${IFS}
INBOUND_FILE="/usr/local/etc/iptables.inbound"
if test -f "${INBOUND_FILE}"; then
  IFS=$'\n'
  for line in $(cat "${INBOUND_FILE}")
  do
    ( echo $line | egrep ^# ) > /dev/null && continue
    proto="$(echo $line | awk -F, '{print $1}')"
    dport="$(echo $line | awk -F, '{print $2}')"
    dipaddr="$(echo $line | awk -F, '{print $3}')"
    if test "${proto}" = "tcp" -o "${proto}" = "udp"; then
      iptables -t nat -A PREROUTING -p "${proto}" -i ppp+ --dport "${dport}" -j DNAT --to-destination "${dipaddr}"
      iptables -A into -p "${proto}" --dport "${dport}" -d "${dipaddr}" -j ACCEPT
    fi
  done
fi
IFS=${OIFS}

iptables -A inppp -j logdrop
...

まとめ

とりあえずここまでのコードの断片を繋げると、それなりに動くものができていると思います。

2009/12/29

Alixで動いているCFカード上のDebianを別カードに引っ越してみる

家のDNSサーバーにしているalixのCFカードは実験的に使った古い512MBのカードだったので手狭になってしまいました。 そこで手持ちのTrancend 2GB 266x (SLC)なCFカードに引っ越す事にしました。

TrancendのCFカードは133xがMLC, 266xがSLCですが、hdparm等でスピード自体に違いがある事はわかりますが、メモリ上で動く小さいサーバープロセスでは体感できるほどの速度差はないと思います。

動かすサーバーの種類に依りますが、耐久性以外の理由でSLCなCFカードを選択する必要性は低いのかもしれません。

待ち受けカードを準備

稼働中の512MB CFカードは最後まで抜かずに、新しく置き換える2GBのCFカードを準備する事にしました。 最終的にはrsyncを使って、ファイルをコピーしようと思います。 新しく準備した2GB CFカードの作業はUbuntu 8.04にUSB接続のCFカードリーダーを接続して行なっています。

GRUBのインストール

とりあえず Ubuntu 7.10からALIXでブート可能なCFカードを作成する の手順に従って、Ubuntu上でgrubを導入するところの作業を行なっています。

認識したCFカードが"/dev/sdg1"だとすると、次のようなコマンドになります。

$ sudo /sbin/mke2fs /dev/sdg1
$ sudo /sbin/tune2fs -c 0 -i 0 /dev/sdg1
$ sudo mount /dev/sdg1 /mnt/cf
$ sudo grub-install --root-directory=/mnt/cf /dev/sdg

rsyncを使ったファイルコピー

稼働中のalixサーバーからファイルをコピーしてくるためには、一般ユーザーIDではアクセスできないファイルが出てきます。 そのためrootでのsshアクセスを一時的に許可することにしました。

root@alix:# vi /etc/ssh/sshd_config
PermitRootLogin yes

上記のように"PermitRootLogin"に"yes"を設定します。 続いて、Ubuntu上で作業を続けます。

$ sudo rsync -av root@alix:/. /mnt/cf/.

"root@alix"はユーザーID:root, ホスト名:alixの意味で、"alix"の部分はIPアドレスにする方が良いかもしれません。(e.x. "root@192.168.1.2")

/procやら/sysは不要なので、 --exclude で指定するべきだったのですが手で削除し、作りかえました。

$ sudo rm -rf /mnt/cf/proc
$ sudo mkdir /mnt/cf/proc

CFカードの交換

こんな感じで、とりあえずDNSサーバーなalixを止めてCFカードを交換しました。

CPU, Memoryに余裕があるので、CFカードのサイズが4倍になったことですし、 いろいろパッケージも導入して試していこうと思います。

2009/12/21

Blogger APIでLabelのリストを取得する

xmlファイルからbloggerに投稿できるようにしたまでは良かったのですが、 自分がどんなラベルを設定しているか覚えているのが難しくなりました。 以前は一覧から選択すれば良かったのですけれが…。 そこで今回はBlogger APIを使って"ラベル"の一覧を取得しようと思います。

ラベルを一括取得するAPIはない模様…

どうやらエントリを引き出して、その中に設定されているカテゴリー情報を抜き出す必要があるようです。 BloggerExample.pyを元に次のようなメソッドを追加して呼び出してみます。

def list_labels(self):
  labels = []
  query = gdata.blogger.client.Query(max_results="1000")
  feed = self.client.get_posts(self.blog_id, query)
  print feed.entry[0].updated
  for entry in feed.entry:
    for category in entry.category:
      labels.append(category.term)
      pass
    pass
  
  ## Generating output by two phase sort.
  l = []
  for label in set(labels):
    l.append(label)
    pass
  for label in sorted(l):
    print label
    pass
  pass

この中で Query オブジェクトを使っていますが、デフォルト25件までのところ最大1000件までのブログエントリを取って、その中にあるラべル情報を取り出しています。 1000件の中に含まれていないラベルなら使わなくて良いでしょうけれど、最後までループを回す方法が、このAPIの中にはないので、こういう実装になっています。

実は gdata.clinent.GDClient::get_next() の仕組みを使えばループは出来るんですが、 Query を使うのと大差はないので止めました。

クラシックテンプレートで使えるもう一つの方法

この画面にも右側にラベル一覧が表示されているので、DOM+XPathで解決じゃないかな、 とか思いつつ無視して一気に進みました。

ちなみにこのLabels一覧は Jason Sutter さんの方法でJavaScriptを使って生成しています。 その中でアクセスしているURLからラベルの一覧を取得するURL、 http://www.blogger.com/feeds/USERID/blogs/BLOGID 、を使います。

Ubuntu 8.04に入れている xgrepawk を使って、次のようにラベルのリストをコマンドラインで取る事ができます。

$ wget -O- http://www.blogger.com/feeds/USERID/blogs/BLOGID| xgrep -x '//@term' | awk -F= '/term/ {print $2}'
"趣味"
"プログラミング"
...

解決策はいろいろありますが、まぁこれでラベルをちゃんと付けるようにしましょう。 でもuserIDが漏洩してしまうと、いくつか問題がありますね。 まぁ隠している情報じゃないからいいけれど、そのうちテンプレートを直しましょう。

2009/12/24追記:
クラシックテンプレートを使っているとラベル一覧を表示する方法がなかったので、JavaScriptを使ってラベルを表示していましたが、テンプレートをアップグレードして止めました。 上記URLにはラベル以外の情報も含まれています。 特に隠す情報でもなかったのですが、一般的には良くない方法といえると思います。

誰かの作業内容をモニターするというアイデア

ImpressのGAME Watchの記事の中で SEGAのクローンモニタリングという教育手法について紹介されていました。

これはSEGAがSIGGRAPH ASIA 2009で行なったセッションの中でアートスタッフの新人教育の方法について説明していたもので、メインモニタの出力をスプリットして新人と先輩社員のセカンダリモニタに互いに表示させるという手法です。

原始的な手法ですが、概ね実施当初の成果は良いようです。 慣れてしまうと画面をみないという欠点にも言及されていましたが、新人が熟練者の仕事内容をみることができるというのは良いでしょうね。

自分の中に作っていく成長モデル

私はゲーム業界とは関係ないところで働いていましたが、 一定レベルの技術的スキルが必要とされる現場では、 新人を含めて社員のスキル向上に外部から働きかける事は難しいと感じていました。

若い間は目の前の仕事を片付けるだけで精一杯かもしれませんが、 そこに自分なりの工夫を入れていけると楽しくなると思います。

私は他の人からみてつまらないと思われているような仕事をするときは、 新しいプログラミング言語を使うとか、データ処理用のツールを作るとか、何か1つ自分なりに新しい事を組み込むようにしていました。 「○×縛り」みたいに自分に制約を付けると楽しくはないけれど、辛さは少しだけ軽くなります。 そんな様子は他の人にみえても良かったのかもしれません。

SEやプログラマーの教育には、そのままだと使えないかなとは思いましたが、 何事も新鮮にみえる新人の間に仕事の様子を互いに見えるようにするのは良いアイデアですね。

2009/12/14

ひさしぶりにコンテンツを作ってみた

簡単なXML→HTML変換器を作成したので、調子に乗ってテストを兼ねて気になっていた話題で文書を作成してみました。 実際に使ってみるといろいろ不備がみつかったのですが、その場で修正していって、自分が書きたいものに特化しているけれど、そこそこ使えそうな形になってきたと思います。

きっとベースは同じでも書きたいものによって、少しずつ実装を変化させていくと思います。 書く量を減らしつつ、便利なものを短時間で作っていく、そんな事を目指しています。 対象によってタグが違うのは不便そうですが、記述する対象によってボキャブラリーやタグ付けの粒度は変化すると思うんですよね。

このブログでは読書感想文的なものは単純なリンクを含む程度で、 本文で必要なものは 強調 ぐらいです。 またコードや端末への入力・出力といった技術的な要素を多く含むので、codeタグといったXHTMLへ直接マッピングされるタグの他にコメント用の"note"タグという独自のタグも含んでいます。

今回使ったVelocityベースのAnakiaフレームワークはXSLTのように標準化がされていないけれど、 小さくまとまっていますから自分のツールボックスには入れておこうと思います。

いくつか実装した機能として、本文中に直接anchorとhrefを埋め込む事はせずに、外部のXMLファイルに情報をまとめて、本文からは簡単な名前だけを書き、Anakiaの中で変換するようにしています。 XMLファイルは、build.xmlの中でcontextタグの中で指定して、参照用のマクロをsite.vslで定義しています。 応用すれば、いろいろな情報と本文との結び付きを弱く保つ事ができそうです。

build.xmlでの指定例

<anakia>
...
  <context name="anc" file="../etc/proj_anchor.xml" />
</anakia>

ここで指定した"proj_anchor.xml"ファイルの中身は次のようになっています。

<anchor>
  <item name="ibm.jp" href="http://www.ibm.com/jp"/>
</anchor>

build.xmlの中で指定したcontextには次のようなVelocityマクロを準備しました。

#macro (find_anc $name $value)
  #set ($href = $anc.selectNodes("/item[@name='$name']/@href"))
  <a $href>$value</a>
#end

丁度このブログ記事を生成するツールには、この仕組みを入れていなかったのでバックポートしてみました。 ブログの中にあるリンクを一元管理するのは良いのか、悪いのか、どうなんだろうな。

しかし自分で作成したツールの仕様書をどの程度のレベルでまとめておくのか、少し悩んでいます。 okwaveでも似たような投稿は度々みますね。良い結論に至ったものは、まだなさそうですが。 まぁ基本的な取扱説明書と内部のデータ構造ぐらいは最低限かな。

今回作成したツールで書いた文書はこちら → http://www.yasundial.org/shell_script/

2009/12/12

Apache Velocityの困った挙動

VelocityでXPathを使うと、いろいろ困った事が起ります。 結局XPathを使うのは止めて、JDOMの範囲で処理をするようにしました。

テキストノードが引き出せないXPath

通常はタグの中にあるテキストは $node.selectNode("/element::text()") のような構文で取り出す事ができるのですが、Velocityの場合は text() の部分がエラーになります。 そのためテキストノードに到達するためには、 $node.getChild("element").getContent() のような形でアクセスする他に方法がありません。

この例だとそんなに問題がないように思えますが、特定の要素のアクセスする場合に差が出ます。 XPathの持つ /element[@attr='val'] の構文はループを使わずに特定の要素にアクセスができますが、Velocityの場合は #foreach を使い、 #if('val' == $item.getAttributeValue("attr")) のように条件にマッチする場合に処理を行なう必要があります。

結局要素全体にアクセスしたい場合に限定して使う事になるのですが、後述するように他にも問題があります。 XPathの構文を使わないようにするのも、少し冗長でも分かり易くみえていいのですが、期待してたので少し残念です。

日本語が使えないXPath

次に .selectNode("/chapter[@name='はじめに']") のように式の中に日本語を書くとエラーになります。 antlr-2.7.5.jarがバイト単位で処理しているのが原因なのかなぁ。日本語パッチがあるようなので最近のものには含まれているのかなと思い、jarファイルを最新の"antlr-3.2.jar"に差し替えてみましたが、同じエラーになりました。

最近はUTF-8に対応している字句解析器もありますが、日本語を通すのはやはり難しい場合が多いですね…。

対応自体はXPathを使わずに、JDOMの範囲で $node.getChild("chapter").getAttributeValue("name") == "はじめに" のようなコードを書けば問題なく動きます。 間違いなく意図した要素を指定するには、場合によって少し面倒ですが、丁寧に書きつつコメントで補うしかないかなぁ。

最後に

全体的にxpathはおまけの印象が強いでが、それでもVelocityは便利だと思います。 フレームワークは制限が多少きつめな方が集中できて良いのかもしれないですね。

2009/12/11

Ubuntu 8.04 LTSでGoogle Blogger APIを触ってみる

何はなくとも Blogger API の"client libraries"からpython用のファイルをダウンロードしておきます。 Blogger APIの文書自体は参考になりますが、 サンプルコードに手を加えていけば丁寧に読まなくても先に進めると思います。

パッケージの展開とビルド

Ubuntu 8.04のパッケージに準備されているpython-gdataパッケージはバージョンが1.0です。 そもそも現在の最新版は2.0.5ですから、附属のサンプルは動きません。 ダウンロードしてきたzipパッケージを展開して必要なライブラリをビルドします。

$ unzip -x gdata-2.0.5.zip
$ cd gdata-2.0.5
$ python setup.py build

インストールしてしまうと/usr以下にコピーされてしまいますから、 今回はそれを行なわずにサンプルプログラムを動かす事にします。 まずは附属のサンプルプログラムを起動してみましょう。

$ cd samples/blogger
$ python BloggerExample.py 
Traceback (most recent call last):
  File "BloggerExample.py", line 35, in <module>
    import gdata.blogger.client
ImportError: No module named blogger.client

普通に動かすと"blogger.client"つまり"blogger/client.py"ファイルが見つからないと言われてしまいます。client.pyを探すと…

$ find ../../ -name '*.py' | grep blogger/client
../../build/lib/gdata/blogger/client.py
../../src/gdata/blogger/client.py

今回はbuildディレクトリの方を使うので、コマンドラインを修正します。

$ env PYTHONPATH=../../build/lib python BloggerExample.py
Please choose the authorization mechanism you want to use.
1. to use your email address and password (ClientLogin)
2. to use a web browser to visit an auth web page (AuthSub)
3. if you have registed to use OAuth
: 1
Please enter your username: xxx@gmail.com
....

基本的には BloggerExample.py の中で必要そうな処理はメソッド単位でまとめられているので通して眺めるぐらいで必要な知識は手に入ると思います。

ブログ記事の更新

BloggerExample.py にない機能はブログ記事の更新でしょうか。 でもタイトルの更新は BloggerExample.py に書かれているので、 適当に "Title" を "Content" に書き換えたてうまく動いています。

for article in self.client.get_posts(self.blog_id):
  ...
  article.content = atom.data.Content(type='text', text=content)
  ...

"Developer's Guide: Python"の" Publishing a blog post "には"atom.Content"という記述がありますが、"atom/data.py"を読む限りだと動かないですよね…。

Blogger APIは全体的に動きから予測できるのだけれど、細かいところで初級者に優しい作りではないようです…。

Bloggerへの投稿を自動化してみた

HTMLファイルを生成する事はできるようになったので、Google Data ProtocolのPython Client Libraryに附属するBlogger用のサンプルスクリプトを修正してコマンドラインから投稿できるようにしました。

この投稿自体も自前のXMLファイルから一連のコマンドでブログに投稿しています。 また初投稿した時にタイトル名とblog_post_idを保存しておくようにしたので、 タイトルの変更や記事の更新もできるようになっています。

実行時の様子

動作は2段階でXMLからHTMLに変換する部分( build.sh )と、生成したHTMLファイルをリストして指定した記事を投稿する部分( run.sh )に分かれています。

記事生成スクリプトの実行

HTMLファイルを生成するといっても実体はxhtmlに合わせているのでXML文書です。 ANTタスクの前後にxmlvalidateタスクを実行するようにしているので、少なくともwell-formedなXML文書である事は最低限チェックしています。

Buildfile: prop/build.xml

validate_xml:
[xmlvalidate] 2 file(s) have been successfully validated.

docs:
   [anakia] Transforming into: /home/user01/blogpost/html
   [anakia] Input:  20091211.1.xml
   [anakia] Output: /home/user01/blogpost/html/20091211.1.html
[xmlvalidate] 2 file(s) have been successfully validated.

BUILD SUCCESSFUL
Total time: 0 seconds
投稿スクリプトの実行

[0]	Bloggerへの投稿を自動化してみた
[1]	Apache Anakiaを使った簡易CMSの作成
select article number: 0
find entry: 9106769796720729015
[update_article] file=20091211.1.html,title=Bloggerへの投稿を自動化してみた,labels=[u'web', u'\u30d7\u30ed\u30b0\u30e9\u30df\u30f3\u30b0', u'ubuntu']

さいごに

まだ記事を参照するためのリンクの仕組みはなく、編集を楽にするツール類の開発は必要だと思います。でもタイトルの変更と記事の更新ができるようになった事で最低限の機能は実装できました。

しばらく使ってみて、これをネタにアプリケーションの設計や文書化について勉強していこうと思います。

2009/12/10

Apache Anakiaを使った簡易CMSの作成

anakiaについて調べてみたので、このブログに投稿する記事を生成するための簡易CMSを作ってみました。 機能は少ないですし、bloggerにポストするところも手でコピー&ペーストしています。

設計方針

このブログでは端末への入力やその結果を表示したり、 アプリケーションコードの一部を書く事が多いです。

いままでは手動で&などとクォートしていたのですが、 量が多いと面倒な事になります。 リダイレクト(>)なんかは手で書いていると、向きを間違えそうですしね。

コードを記述する場合の例

パラグラフ'p'タグの外で'code'タグやら'kbd'タグを使う場合は、'pre'タグの中に入れています。 この場合は主にCDATAを使って生のコードをそのまま書いています。

変換前:元のテキスト

<code><![CDATA[
<body>
  <h1>title</h1>
  <p>本文</p>
</body>
</code>]] >

変換後:画面には次のように表示される

<body>
  <h1>title</h1>
  <p>本文</p>
</body>

'p'タグの中でも'code'タグや'kbd'タグを使うことが出来るようになっています。 コマンドの入力$ emacs 20091210.1.xml &、コードの断片int i=0;

anakiaを使う場合の注意

Apache Anakiaをダウンロードして中にあるサンプルを変換して試すと思うのですが、 試す前に挫折してしまうかもしれません。 コマンドラインで動かそうとするとCLASSPATHの設定や附属のbuild/build.xmlはanakiaのビルド用でドキュメントの変換用ではなかったりします。

あとはデフォルトの言語が"ISO-8859-1"なので、せめてUTF-8なら良いのですが環境に合わせて適切に設定する必要もあります。

ディレクトリ構造

トップディレクトリは任意で、そこからの相対的なパスは次のようになっています。

  • apache-ant-1.7.1
  • anakia-1.0
  • xml/20091210.1.xml - 記事ファイル
  • build.sh - antを呼び出すラッパースクリプト
  • html - 出力用ディレクトリ
  • prop - 設定用ファイルを配置するディレクトリ
  • prop/build.xml - ant用設定ファイル
  • prop/site.vsl - 変換ルールを記述した
  • prop/project.xml - 今回は使わない共通テンプレートファイル
  • prop/velocity.properties - 日本語を使うならセットアップ必須
build.sh

CLASSPATHの設定が面倒なので、wrapperスクリプトを作成しました。

#!/bin/bash

BASEDIR="$(dirname $0)"
ANT_HOME="${BASEDIR}/apache-ant-1.7.1"
ANAKIA_HOME="${BASEDIR}/anakia-1.0"

## export essential variables
export ANT_HOME
export PATH="$ANT_HOME/bin:$PATH"

CP_ANT=$(find . $ANT_HOME/lib -name '*.jar' | tr '\n' ':')
CP_ANAKIA=$(find . $ANAKIA_HOME -name '*.jar' | tr '\n' ':')
export CLASSPATH=$CLASSPATH:$CP_ANT:$CP_ANAKIA

## main ##
cd "$BASEDIR"
ant -f prop/build.xml
prop/build.xml

マニュアルに附属のものをベースに変更しています。 書き方はいろいろやってみないと良く分からないですね。 "projectFile"は"${docs.src}"からの相対パスになりますが、"style"の方は先頭で指定した"${basedir}"からのパスになったりするのでパスの指定は何回かやり直しました。

anakiaに附属の例では"AnakiaTask"クラスがない場合にエラーにしていましたが、 ここではxmlファイルがwell-formedでない場合にはエラーを出すようにしています。

<project name="build-site" default="docs" basedir="..">
  <property name="docs.src" value="xml"/>
  <property name="docs.dest" value="html"/>

  <target name="validate_xml">
    <xmlvalidate failonerror="yes" lenient="yes" warn="yes"
		 classname="org.apache.xerces.parsers.SAXParser">
      <fileset dir="${docs.src}" includes="**/*.xml"/>
      <attribute name="http://xml.org/sax/features/validation" value="false"/>
      <attribute name="http://apache.org/xml/features/validation/schema"  value="false"/>
    </xmlvalidate>
  </target>
  <target name="docs" depends="validate_xml">
    <taskdef name="anakia"
	     classname="org.apache.anakia.AnakiaTask"/>
    <anakia basedir="${docs.src}" 
	    destdir="${docs.dest}"
	    extension=".html" style="prop/site.vsl"
	    projectFile="../prop/project.xml"
	    lastModifiedCheck="true"
	    velocityPropertiesFile="prop/velocity.properties">
    </anakia>
  </target>
</project>
prop/velocity.properties

linux上で動かしているのでUTF-8に設定しています。 WindowsであればShift_JISなどに変更するのが良いのでしょうね。

input.encoding=UTF-8
output.encoding=UTF-8

さいごに

anakiaは便利なんですけれど、含まれているsite.vslファイルの中では'source'タグを処理する時に'pre'開始タグがなくて、'/pre'閉じタグだけがあったり、急いでリリースしたのかなぁと思ったりします。

入出力の言語設定はvelocityの部分なので、anakiaのマニュアルには記述がありません。 それでもコードをみてvelocity.propertiesだなと当りが付けられるので、OSSで良かったです。

Velocityを直接操作しても、この程度のものは作れると思いますが、 このanakiaを触り始めて、この記事を書き上げるところまで睡眠時間を含めて通算15時間ぐらいですから、 まぁ自分の時間は節約できたと思います。 シンプルだけれど、フレームワークとしては初心者向けのはずなのに、説明不足な点が少しばかり残念です。

2009/12/10追記: 後から付けたすためのノートのテスト。

2009/12/07

Ubuntu Enterprise Cloudに附属のイメージが起動しない

結局のところUbuntu Enterprise Cloud(UEC)で"euca-debian-5.0-x86_64.tar.gz"は起動しませんでした。

同じくWebインタフェースの"Extras"からダウンロードした"euca-ubuntu-9.04-x86_64.tar.gz"は、無事に起動しています。 Googleで検索すると似たような現象に遭遇している方はいるようですが、やはり動く環境と動かない環境があるという事のようです。

[    6.393022] sd 2:0:0:0: Attached scsi generic sg1 type 0
Done.
Gave up waiting for root device.  Common problems:
 - Boot args (cat /proc/cmdline)
   - Check rootdelay= (did the system wait long enough?)
   - Check root= (did the system wait for the right device?)
 - Missing modules (cat /proc/modules; ls /dev)
ALERT! /dev/sda1 does not exist. Dropping to a shell!

このエラーメッセージの中にあるように"rootdelay"を設定してみましたが、busy-boxが立ち上がるまでの時間が長びくだけで、問題は解決しませんでした。

kernelのcmdlineを変更する方法

Eucalyptusの仕組みでkernelのコマンドラインを変更して"rootdelay=90"を追加しようとしたのですが、 よくわかりませんでした。 そこでNode Controller上で直接ファイルを編集する事にしました。(フロントエンド側ではありません)

$ diff /usr/share/eucalyptus/gen_kvm_libvirt_xml.20091207 /usr/share/eucalyptus/gen_kvm_libvirt_xml
95c95
<         <cmdline>root=/dev/sda1 console=ttyS0</cmdline>
---
>         <cmdline>root=/dev/sda1 rootdelay=90 console=ttyS0</cmdline>

効果はありませんでしたが、インスタンスを起動すると反映されています。

$ grep Command /var/lib/eucalyptus/instances/admin/i-42C40825/console.log
[    0.000000] Command line: root=/dev/sda1 rootdelay=90 console=ttyS0

この問題は自分でイメージを作成するとか、直接解決する方法は簡単には見付からなそうです…。

@IT::Ubuntuで始めるクラウドコンピューティング

@ITの記事の中でUbuntuで始めるクラウドコンピューティングが公開されています。

これに従えばUbuntu Enterprise Cloud (UEC)環境は作れるのですが、ちょっと使いづらいかなと思いました。 単純にインスタンスを動かしたいだけであれば、”euca-bundle-image”、"euca-upload-bundle"などは使わずにWebインタフェースの"Store"メニューを使うのが簡単そうです。 参照::STEP 6: Install an image from the store

手順自体はEucalyptusにあるドキュメントと同じ構成ですが、シェル変数が多用されているために、何をやっているか読み解くのに時間がかかりそうです。 ただクラウド環境は自動化が肝ではあるので、いろいろな作業を自動化するためにこういう方法は必要で、最終的には慣れる必要があると思います。

事前準備

以下ではUECの"Extras"から”euca-debian-5.0-x86_64.tar.gz”をダウンロードした前提で、どのようにイメージを作成して行くのか試してみようと思います。 tar.gzファイルは展開しておきます。

$ tar xvzf euca-debian-5.0-x86_64.tar.gz
euca-debian-5.0-x86_64/
euca-debian-5.0-x86_64/xen-kernel/
euca-debian-5.0-x86_64/xen-kernel/vmlinuz-2.6.27.21-0.1-xen
euca-debian-5.0-x86_64/xen-kernel/initrd-2.6.27.21-0.1-xen
euca-debian-5.0-x86_64/kvm-kernel/
euca-debian-5.0-x86_64/kvm-kernel/vmlinuz-2.6.28-11-generic
euca-debian-5.0-x86_64/kvm-kernel/initrd.img-2.6.28-11-generic
euca-debian-5.0-x86_64/debian.5-0.x86-64.img

シェルにeucarcファイルを読み込んでおく事と、事前にいくつか準備しておくシェル変数は次のようになっています。設定して別の端末ウィンドウに移ってしまってはシェル変数は引き継がれないので、以下の操作は全部同じ端末ウィンドウの中で実施します。

$ . ~/.euca/eucarc
$ TS=$(date +%Y%m%d%H%M%S)
$ echo $TS
20091207164000

変数を展開した形の解説 〜 EKI変数の設定まで

*http://www.atmarkit.co.jp/flinux/special/eucaly/eucalyc.html より抜粋
# euca-bundle-image -i $UEC_KERNEL -r $IARCH --kernel true
# euca-upload-bundle -b $BUCKET_KERNEL -m /tmp/$UEC_KERNEL.manifest.xml
# EKI=$(euca-register $BUCKET_KERNEL/$UEC_KERNEL.manifest.xml | grep "^IMAGE" | awk '{print $2}') && echo $EKI

"euca-debian-5.0-x86_64.tar.gz"を展開したところから、上記と同じようにコマンドを打つと次のようになります。

$ euca-bundle-image -i euca-debian-5.0-x86_64/kvm-kernel/vmlinuz-2.6.28-11-generic -r x86_64 --kernel true
x86_64
Checking image
Tarring image
Encrypting image
Splitting image...
Part: vmlinuz-2.6.28-11-generic.part.0
Generating manifest
$ euca-upload-bundle -b "k-$TS" -m /tmp/vmlinuz-2.6.28-11-generic.manifest.xml
Checking bucket: k-20091207164000
Creating bucket: k-20091207164000
Uploading manifest file
Uploading part: vmlinuz-2.6.28-11-generic.part.0
Uploaded image as k-20091207164000/vmlinuz-2.6.28-11-generic.manifest.xml
$ euca-register k-$TS/vmlinuz-2.6.28-11-generic.manifest.xml
IMAGE eki-864C132D
$ EKI=eki-864C132D

最後のところは、中身を見せるために少し変更して自分でEKI=eki-864C132Dと代入しています。 "eki-864C132D"の部分は、実行する人によって変化してきます。

変数を展開した形の解説 〜 ERI変数の設定まで

*http://www.atmarkit.co.jp/flinux/special/eucaly/eucalyc.html より抜粋
# euca-bundle-image -i $UEC_INITRD -r $IARCH --ramdisk true
# euca-upload-bundle -b $BUCKET_INITRD -m /tmp/$UEC_INITRD.manifest.xml
# ERI=$(euca-register $BUCKET_INITRD/$UEC_INITRD.manifest.xml | grep "^IMAGE" | awk '{print $2}') && echo $ERI

今回も最後のERI変数への代入のところは分けて実行しています。

$ euca-bundle-image -i euca-debian-5.0-x86_64/kvm-kernel/initrd.img-2.6.28-11-generic -r x86_64 --ramdisk true
x86_64
Checking image
Tarring image
Encrypting image
Splitting image...
Part: initrd.img-2.6.28-11-generic.part.0
Generating manifest
$ euca-upload-bundle -b "r-$TS" -m /tmp/initrd.img-2.6.28-11-generic.manifest.xml
Checking bucket: r-20091207164000
Creating bucket: r-20091207164000
Uploading manifest file
Uploading part: initrd.img-2.6.28-11-generic.part.0
Uploaded image as r-20091207164000/initrd.img-2.6.28-11-generic.manifest.xml
$ euca-register r-$TS/initrd.img-2.6.28-11-generic.manifest.xml
IMAGE eri-C36C141F
$ ERI=eri-C36C141F

変数を展開した形の解説 〜 EMI変数の設定

*http://www.atmarkit.co.jp/flinux/special/eucaly/eucalyc.html より抜粋
# euca-bundle-image -i $UEC_IMG.img -r $IARCH --kernel $EKI --ramdisk $ERI
# euca-upload-bundle -b $BUCKET_IMAGE -m /tmp/$UEC_IMG.img.manifest.xml
# EMI=$(euca-register $BUCKET_IMAGE/$UEC_IMG.img.manifest.xml | grep "^IMAGE" | awk '{print $2}') && echo $EMI

ここまでで"eucarc"ファイルの中で設定されているもの以外に、次のようなシェル変数を設定しています。

TS
20091207164000
EKI
eki-864C132D
ERI
eri-C36C141F
$ euca-bundle-image -i euca-debian-5.0-x86_64/debian.5-0.x86-64.img -r x86_64 --kernel $EKI --ramdisk $ERI
x86_64
Checking image
Tarring image
Encrypting image
Splitting image...
Part: debian.5-0.x86-64.img.part.0
Part: debian.5-0.x86-64.img.part.1
Part: debian.5-0.x86-64.img.part.2
Part: debian.5-0.x86-64.img.part.3
Part: debian.5-0.x86-64.img.part.4
Part: debian.5-0.x86-64.img.part.5
Part: debian.5-0.x86-64.img.part.6
Part: debian.5-0.x86-64.img.part.7
Part: debian.5-0.x86-64.img.part.8
Part: debian.5-0.x86-64.img.part.9
Part: debian.5-0.x86-64.img.part.10
Part: debian.5-0.x86-64.img.part.11
Part: debian.5-0.x86-64.img.part.12
Part: debian.5-0.x86-64.img.part.13
Part: debian.5-0.x86-64.img.part.14
Part: debian.5-0.x86-64.img.part.15
Part: debian.5-0.x86-64.img.part.16
Part: debian.5-0.x86-64.img.part.17
Part: debian.5-0.x86-64.img.part.18
Part: debian.5-0.x86-64.img.part.19
Generating manifest
$ euca-upload-bundle -b "i-$TS" -m /tmp/debian.5-0.x86-64.img.manifest.xml
Checking bucket: i-20091207164000
Creating bucket: i-20091207164000
Uploading manifest file
Uploading part: debian.5-0.x86-64.img.part.0
Uploading part: debian.5-0.x86-64.img.part.1
Uploading part: debian.5-0.x86-64.img.part.2
Uploading part: debian.5-0.x86-64.img.part.3
Uploading part: debian.5-0.x86-64.img.part.4
Uploading part: debian.5-0.x86-64.img.part.5
Uploading part: debian.5-0.x86-64.img.part.6
Uploading part: debian.5-0.x86-64.img.part.7
Uploading part: debian.5-0.x86-64.img.part.8
Uploading part: debian.5-0.x86-64.img.part.9
Uploading part: debian.5-0.x86-64.img.part.10
Uploading part: debian.5-0.x86-64.img.part.11
Uploading part: debian.5-0.x86-64.img.part.12
Uploading part: debian.5-0.x86-64.img.part.13
Uploading part: debian.5-0.x86-64.img.part.14
Uploading part: debian.5-0.x86-64.img.part.15
Uploading part: debian.5-0.x86-64.img.part.16
Uploading part: debian.5-0.x86-64.img.part.17
Uploading part: debian.5-0.x86-64.img.part.18
Uploading part: debian.5-0.x86-64.img.part.19
Uploaded image as i-20091207164000/debian.5-0.x86-64.img.manifest.xml
$ euca-register i-$TS/debian.5-0.x86-64.img.manifest.xml
IMAGE emi-25881166
$ EMI=emi-25881166

登録したイメージのテスト

いままでに他のイメージを起動するなどしていなければ、keypairの作成とルーティングを作成しておきます。 既に実行されているかは”euca-describe-keypairs”を実行して"mykey"が登録されているかどうか、"euca-describe-groups"を実行して"... ALLOWS tcp 22 22 FROM CIDR 0.0.0.0/0"が設定されているかで確認できます。

$ euca-add-keypair mykey > mykey.priv
$ euca-authorize default -P tcp -p 22 -s 0.0.0.0/0

さて、いよいよ起動です。

$ euca-run-instances -k mykey $EMI
RESERVATION r-56B308F1 admin default
INSTANCE i-5EB10A92 emi-25881166 192.168.1.163 172.19.1.4 pending  mykey  0  m1.small  2009-12-07T07:52:16.199Z  yasundial  eki-864C132D  eri-C36C141F

設定されている値やインスタンスの状況を調べたい時には"euca-describe-"で始まるコマンドが使えるようです。 Amazon EC2から引き継いでいるんでしょうけれど、良いネーミングルールですね。

先ほどの"euca-run-instances"の出力では、最初はpendingとなっているままなので、起動していません。 "euca-describe-instances"を定期的に実行して、"running"になる事を確認します。 ubuntu.comのUEC Install手順の中ではUNIX系OSに標準的に存在している"watch"コマンドを使う例が載っています。

$ watch -n5 euca-describe-instances

最終的に失敗してしまう…

running状態にはなるものの、busy-boxに落ちてしまっているので、無事に起動というわけにはいきませんでした。 この原因はまた追い掛けていこうと思います。

その他の注意点

今回導入したイメージの中にはXen用のものも含まれていますが、こちらを使用すると"pending"のステータスから"terminated"となり起動しません。

また"euca-upload-bundle"の"-b"オプションの引数で状況に応じて、"BUCKET_KERNEL","BUCKET_INITRD","BUCKET_IMAGE"の3つの変数を使い分けていますが、 これはprefixと考えて、1つの文字列を使い回すのが良いようです。

ざっとこんな感じで、イメージを起動する事はできましたが、これだけだとあまり活用する目処は立たないですね。 もう少し周辺の情報を集めてみようと思います。

2009/12/06

東京散策 その弐

原宿の他にお台場にも始めて行ってきました。近くの豊洲で働いていたのに、初ゆりかもめ。 事前にGoogleマップで確認していったのですが「ゆりかもめ」じゃなくて「東京臨海新交通臨海線」なんですよね、正式名称は。 とはいえ、行きはりんかい線だったのでまっすぐ徒歩でテレコムセンタービル近くの日本科学未来館へ。

仕分け作業で毛利さんが吊し上げられた曰くつきの建物ですが、 いまある最高ものをみせたい、という趣旨の毛利さんの発言通り、いろいろ触れてすごかったです…。

昔よくいった新潟の自然科学館もけっこう凝った展示をしていたと記憶していますが、期待以上でしたね。

ものによってはちょっと古めかしい感じもしましたが、それでも未来館にいけば自分で動かせる、触れるっていうインパクトはあると思います。 全体的に見せるだけじゃなくて体験できる展示を心がけているのかなぁという印象を強く持ちました。

仕分けの結果で予算の縮減というフレーズだけが取り上げられていますが、 財団を見直すっていう付帯意見が全員から出ているところってあまり取り上げられてないですよね。 それに毛利さんが情熱を持った最高の館長である事に異論はないだろうから、彼をバックアップできる体制をどうやって作っていけるか、仕分けに参加した識者の方々はその方面からも手伝うべきでしょう。

仕分け人も必要だけど、具体的な手段を作っていくためのシンクタンクも必要だなぁ。 それにいまどき修学旅行でTDLに行くぐらいなら未来館にすればいいのに。

きっとまた行くだろうし、その時は一人じゃないでしょう。つまらないから。

2009/12/05

東京散策

今回の旅行ではマイコミジャーナルの記事をみてJohn Squireさんの個展をにいってきました。 というか、原宿という街が自分に合うわけもない…。いたたまれなくなって明治神宮に逃げ込んできました。

さて別にファンっていうわけではないし、以前から注目していた人というわけでもない。 改めてYouTubeでいくつかライブ映像などを見たけれど、自分には合わない音楽だなと思いました。

それでも足を運んだ理由は単純にギターから絵に移っていったという経緯に興味をもったのと、インタビュー記事で率直に発言している様子に魅かれたから。

作品をみてタイトルとイメージが重なったものは多くはなかったけれど、心に残るものも何点かありました。それでも全般的には「やっぱり分からん」って感じでしたけど、好きな雰囲気で良い時間を過ごせました。

作品に付けられた値段はとても高いと思いましたが、好きな人が買えているのかが気になります。

ここからは、また別の話し…

そんなこんなで絵以外で印象に残っていたのが、片隅に置かれた古いiMacでのBBCインタビュー映像の一コマ。音楽を作っていた頃の話しの中で出た"de-construct"と"steal"という言葉。 文脈は関係なくて、この言葉が妙にひっかかっているんですよね。

一般的に音楽を分析的に扱えば似ている点は抽出できるし、 実際に一度コピーしてからメロディーやリズムをいじったのが分かるようなものも世の中には溢れています。

楽曲を分析して批評する人もいるけれど、時々手法が強引で、高次ではまったく違うものを低次に投影して同じといっているような違和感を感じる事があります。

作り手にしたら何がオリジナルなのか、その条件に悩むものなのかもしれません。 経験もなく無から何かを作るっていうのは有り得ない行為ですからね。

絵は有形なもので制約が強いように思えるけれど、無形な音よりも自由な世界なのかもしれない、そんな風に感じました。