Mac OS X Yosemite からの NFS 経由でのNASへの書き込みが激遅なのを何とかする

Solarisとのファイル共有も考えてNFSが喋れるNAS(NETGEAR ReadyNAS)を買ったのだけど、Mac からの NFS の書き込みが激遅なのでなんとかしてみた。

改善前の状態

MacNAS 間ギガビットでつながっているにも関わらず、スループットが数Kbps程度で、以下のエラーが延々と繰り返す。

nfs server readynas:/data/Documents: not responding
nfs server readynas:/data/Documents: is alive again

ついカッとなって Wireshark でパケットを調べた所、Mac からの NFS の COMMIT リクエストに対する NAS側からのリプライに時間がかかっている(30秒超!!)ことがわかった。

対策

NFS マウント時に async オプションを追加し、データの WRITE 後に COMMIT リクエストを出さないようにする。

1) /etc/nfs.conf に以下の行を追記し async オプションの使用を許可する
nfs.client.allow_async = 1
2) /etc/fstab を作成し NFS マウントのエントリを追加する。その際マウントオプションとして async を指定
readnas:/data/Documents /data nfs async

※ 念のため、/data はマウントポイントで予め mkdir(1) コマンドでディレクトリを作っておく必要があります。また、readnas は 私の環境での NFS のサーバ(NAS)のホスト名です。/data/Documents はNFSサーバ(NAS)上で共有されているディレクトリです。

3) root ユーザで automount(1) コマンドを使って fstab ファイルの内容をロードする
$ sudo /usr/sbin/automount -vc

対策2

起動時に自動マウントする必要はない場合は、手動でマウントするだけであれば以下のコマンドでも async オプションをつけてマウントできます

$ sudo mount_nfs -o async,nosync readynas:/data/Documents /data

もちろん不要になったらアンマウントもできます。

$ sudo diskutil umount /data

結果

スループットが 400Mbps 程度に改善!

注意

async オプションを使って COMMIT リクエストを抑制しているため、万一ファイル転送後にディスクへの書き込み前にサーバ側がクラッシュした場合、データが失われる可能性があります。
このへんは覚悟の上、許される環境であれば設定を行ってください。

そもそも、NFS COMMIT リクエストのリプライに30秒かかるNASへがおかしいのじゃないか?とも思えるけど、そんなクリティカルな環境でもないのでとりあえずこの結果に満足。
(NASの購入費が無駄にならなかった的な意味で)

Visual Studio 2013 で emacs キーバインドを使う

Visual Studio 2010 ではEmacsEmulationsという拡張機能が提供されていてEmacsキーバインドが使えたんだけど、Visual Studio 2013 ではこの機能拡張が通常は使えないので、これを使えるようにしようという話です。

元ネタはこちらの stackoverflow のQA

Emacs Keybindings in Visual Studio 2012 or 2013 - Stack Overflow

ぶっちゃけ一番の解答とコメントの補足を合わせたて日本語化しただけですのでこのQA直接見るだけでもOKです。

環境

私が確認した環境は以下のとおり

Windows Windows 7 Professional SP1 + IE 11
Visual Studio Visual Studio 2013 Premium Edition

手順

1. EmacsEmulations をダウンロード
2. ダウンロードした EmacsEmulations.vsix ファイルの名前を EmacsEmulations.zip に変更して zip ファイルとして解凍する。
3. 解凍したフォルダに含まれるextensions.vsixmanifestファイルをテキストエディタ等で開き タグの値を「10.0」から「12.0」に変更する。
      <VisualStudio Version="12.0">
        <Edition>Pro</Edition>
      </VisualStudio>

Visual Studio 2012の場合、値は「11.0」だそうです。私は未確認です。

4. 解凍したフォルダに含まれるEmacs.vskを「C:\Program Files\Microsoft Visual Studio 12.0\Common7\IDE」フォルダにコピーする。(※ 64ビットWindows の場合は「C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE」フォルダにコピー)
5. extensions.vsixmanifest を含む解凍したフォルダに含まれるファイルを再度 zip 圧縮する。

この際、解凍した EmacsEmulations フォルダを圧縮するのではなく、EmacsEmulationsフォルダに含まれる全てのファイル、フォルダを選んで zip ファイルを作るのが大事。
f:id:kaizawa2:20141223165113p:plain

6. EmacsEmulations.zipの拡張子を再びEmacsEmulations.vsixに変更
7. 管理者として実行したコマンドプロンプからEmacsEmulations.vsixを実行
C:\>start EmacsEmulations.vsix

もし「このVSIXパッケージは、ルートファイルextension.vsixmanifestが含まれていないため無効です。VSIXファイルが破損している可能性があります」というエラーが出た場合には手順5.のフォルダごと圧縮してしまったことが原因と思われます。解凍した中身のファイルとフォルダを全て選択してZip圧縮してみてください。
f:id:kaizawa2:20141223170616p:plain

6. 管理者として Visual Studio 2013 を起動し、ツール(T)→オプション(O)からオプションのウィンドをを開き、環境→キーボードで現れる「次の追加キーボードマップスキームを適用」オプションから「Emacs」を選択

f:id:kaizawa2:20141223171958p:plain

これで Emacs キーバインドが使えるようになります。

GlassFish 付属の JavaDB(Derby) 10.9.1.0 を Java7u51 で起動する

Java7u51の Socket Permission の変更によって49152未満のポート番号でソケットを Listen する場合には明示的にパーミッションを与えることが必要になったようです。

参考: Java™ SE Development Kit 7 Update 51 Release Notes

このため、デフォルトでTCP ポート1527でリッスンする JavaDB も以下のようなエラーを吐いて起動できなくなりました。

C:\glassfish4\glassfish\bin>asadmin start-database
Starting database in Network Server mode on host 0.0.0.0 and port 1527.
Unable to start database.  Please check log in C:\glassfish4\glassfish\databases\derby.log.
Command start-database failed.

実際のログファイルの中はこんな感じ

Sun Feb 02 06:29:10 UTC 2014:
Booting Derby version The Apache Software Foundation - Apache Derby - 10.9.1.0 - (1344872): instance a816c00e-0143-f149-323a-00000c6e8579 
on database directory C:\glassfish4\glassfish\databases\dnd_db  with class loader sun.misc.Launcher$AppClassLoader@1860045 
Loaded from file:/C:/glassfish4/javadb/lib/derby.jar
java.vendor=Oracle Corporation
java.runtime.version=1.7.0_51-b13
user.dir=C:\glassfish4\javadb\bin
derby.system.home=C:\glassfish4\glassfish\databases
Database Class Loader started - derby.database.classpath=''
Sun Feb 02 07:34:10 UTC 2014 : Security manager installed using the Basic server security policy.
Sun Feb 02 07:34:11 UTC 2014 : access denied ("java.net.SocketPermission" "localhost:1527" "listen,resolve")
Sun Feb 02 07:34:11 UTC 2014 : access denied ("java.net.SocketPermission" "localhost:1527" "listen,resolve")
java.security.AccessControlException: access denied ("java.net.SocketPermission" "localhost:1527" "listen,resolve")
	at java.security.AccessControlContext.checkPermission(AccessControlContext.java:372)
	at java.security.AccessController.checkPermission(AccessController.java:559)
	at java.lang.SecurityManager.checkPermission(SecurityManager.java:549)
	at java.lang.SecurityManager.checkListen(SecurityManager.java:1134)
	at java.net.ServerSocket.bind(ServerSocket.java:375)
	at java.net.ServerSocket.<init>(ServerSocket.java:237)
	at javax.net.DefaultServerSocketFactory.createServerSocket(ServerSocketFactory.java:231)
	at org.apache.derby.impl.drda.NetworkServerControlImpl.createServerSocket(Unknown Source)
	at org.apache.derby.impl.drda.NetworkServerControlImpl.access$000(Unknown Source)
	at org.apache.derby.impl.drda.NetworkServerControlImpl$1.run(Unknown Source)
	at java.security.AccessController.doPrivileged(Native Method)
	at org.apache.derby.impl.drda.NetworkServerControlImpl.blockingStart(Unknown Source)
	at org.apache.derby.impl.drda.NetworkServerControlImpl.executeWork(Unknown Source)
	at org.apache.derby.drda.NetworkServerControl.main(Unknown Source)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:606)
	at com.sun.enterprise.admin.cli.optional.DerbyControl.invokeNetworkServerControl(DerbyControl.java:158)
	at com.sun.enterprise.admin.cli.optional.DerbyControl.main(DerbyControl.java:245)
java.security.AccessControlException: access denied ("java.net.SocketPermission" "localhost:1527" "listen,resolve")
	at java.security.AccessControlContext.checkPermission(AccessControlContext.java:372)
	at java.security.AccessController.checkPermission(AccessController.java:559)
	at java.lang.SecurityManager.checkPermission(SecurityManager.java:549)
	at java.lang.SecurityManager.checkListen(SecurityManager.java:1134)
	at java.net.ServerSocket.bind(ServerSocket.java:375)
	at java.net.ServerSocket.<init>(ServerSocket.java:237)
	at javax.net.DefaultServerSocketFactory.createServerSocket(ServerSocketFactory.java:231)
	at org.apache.derby.impl.drda.NetworkServerControlImpl.createServerSocket(Unknown Source)
	at org.apache.derby.impl.drda.NetworkServerControlImpl.access$000(Unknown Source)
	at org.apache.derby.impl.drda.NetworkServerControlImpl$1.run(Unknown Source)
	at java.security.AccessController.doPrivileged(Native Method)
	at org.apache.derby.impl.drda.NetworkServerControlImpl.blockingStart(Unknown Source)
	at org.apache.derby.impl.drda.NetworkServerControlImpl.executeWork(Unknown Source)
	at org.apache.derby.drda.NetworkServerControl.main(Unknown Source)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:606)
	at com.sun.enterprise.admin.cli.optional.DerbyControl.invokeNetworkServerControl(DerbyControl.java:158)
	at com.sun.enterprise.admin.cli.optional.DerbyControl.main(DerbyControl.java:245)

今後 JavaDB 側でも対応してくるとは思いますが「なんでもいいからとりあえず起動したい」という方のために私の行った対策を方法を書いておきます。

なお、以下の手順では以下のフォルダに GlassFish がインストールされていると想定しています。これはデフォルトのフォルダですが、もし変えている場合には逐次読み替えてください。

  • C:\glassfish4

※ ちなみに、私の環境は Windows Azuer 上の Windows Server 2012 Datacenter ですがこちらはあまり影響していないと思います。

1. セキュリティポリシーファイルの作成

C:\glassfish4\javadb\bin\security.policy

grant
{
     permission java.net.SocketPermission "localhost:1527", "listen";
     permission java.security.AllPermission;
};

2. JavaDB のJava起動オプションの追加

C:\glassfish4\javadb\bin\NetworkServerControl.bat (Linux の場合には NetworkServerControl)に以下のJava起動オプションを追記します。

セキュリティ・マネージャを有効にする
-Djava.security.manager
セキュリティ・ポリシーファイルのパスの指定
-Djava.security.policy=security.policy

※ ここでは相対パスでカレントディレクトリのファイルを指定しています

Databaseが置いてあるフォルダのパスの指定
-Dderby.system.home=C:\glassfish4\glassfish\databases

※ ここではデフォルトの Dabase の場所を指定しています。Dabaseファイルを別の場所に置かれている場合にはそちらを指定してください

ファイルの実際の行は以下のようになります。
NetworkServerControl.bat

  ...
:runNoClasspath
"%_JAVACMD%" %DERBY_OPTS% -Dderby.system.home=C:\glassfish4\glassfish\databases -Djava.security.manager -Djava.security.policy=security.policy -classpath "%LOCALCLASSPATH%" org.apache.derby.drda.NetworkServerControl %DERBY_ARGS% %DERBY_CMD_LINE_ARGS%
goto end
  ...

3. DERBY_HOME 環境変数のセット

C:\> set DERBY_HOME=C:\glassfish4\javadb

4. JavaDB の起動

先ほど編集したNetworkServerControlでJavaDBを起動します

C:\glassfish4\javadb\bin>NetworkServerControl start
Sun Feb 02 08:14:45 UTC 2014 : Apache Derby Network Server - 10.9.1.0 - (1344872) started and ready to accept connection
s on port 1527

これで無事ポート1527 を Listen して起動できました!

Solaris 用 Twitter ファイルシステム

Twitterファイルシステムとしてマウントする擬似ファイルシステムです。
任意の場所にマウントすると 「home」, 「mention」 といったタイムライン名を持つファイルが現れて cat(1M) や tail(1M) などのコマンドでタイムラインを表示することができます。また vi(1M)、emacs(1M)、もしくはリダイレクトを使って「post」ファイルに書き込めば通常のファイル操作だけでツイートすることもできます。
English Here.

f:id:kaizawa2:20140102225146p:plain

カーネルモードの擬似ファイルシステムがありユーザモードデーモンと協調して動作することで実現しています。簡単に言うと FUSE みたいなものです。

前提条件

1. IUMFS ファイルシステムモジュール

カーネルモードのファイルシステムモジュールが必要になります。
以下のリンクからダウンロードしてビルド、インストールしてください。
kaizawa-iumfs.tar.gz

IUMFS について詳しくはこちらをご覧ください。

2. JRE7/JDK7 以降。

3. Twitter アカウント

ダウンロード

最新の圧縮ファイルを以下のリンクからダウンロードできます。

kaizawa-iumfs-twitterfs.tar.gz

ソースコード

ソースコードGithub で管理していますのでこちらから参照できます。
https://github.com/kaizawa/iumfs-twitterfs

コンパイルとインストール

ユーザモードデーモンプログラム

ユーザモードデーモンプログラムはJavaで書かれています。
上にリンクの圧縮ファイルの中にはコンパイル済みの jar ファイルが含まれていますので基本的には Java のコードをコンパイルする必要はありません。ただ、もし必要であれば以下のように ant コマンドでコンパイルすることもできます。

sol11:/var/tmp/iumfs-twitterfs $ ant
Buildfile: build.xml

jar:

init:

compile:
    [javac] Compiling 107 source files to /var/tmp/iumfs-twitterfs/cmd/build/classes

jar:
      [jar] Building jar: /var/tmp/iumfs-twitterfs/cmd/twitterfsd.jar

BUILD SUCCESSFUL
Total time: 2 seconds
sol11:/var/tmp/iumfs-twitterfs $ 

使い方

マウント方法

方法1) Twitterファイルシステムをオートマウントを利用してマウントする(推奨)

オートマウントを利用して自動的にマウントさせることができます。この方法なら管理者権限で mount(1M)コマンドをマニュアルで実行する必要はありません。
mount(1M) コマンドでのマウントと違い、一般ユーザは管理者の介入なしで自分の Twitter アカウントをセットアップすることができます。

圧縮ファイルの中に含まれる setup_automount というシェルスクリプトを管理者権限で実行してください。オートマウントの設定が自動的に行われます。

# ./setup_automount

何をしているか気になる方はこのスクリプトを見て自分で各コマンドを実行してください。
簡単に言うと automounter マップファイルを作成してマウントポイントが置かれる /twitter ディレクトリを作成し autofs サービスをリスタートしています。

方法2) mount(1M)コマンドを使ってマウントする

オートマウントを利用せずに直接 mount(1M) コマンドを使ってマウントすることもできます。
マウントする際はファイルシステムタイプとして「iumfs」を指定してください。
IUMFS ファイルシステムモジュールをインストールしていれば iumfs 専用の mount コマンドもインストールされており、ファイルシステムタイプをしていすることでこちらが自動的に使われます。

(注) オートマンとを利用するう場合と違い、複数の twitter アカウントを使ってタイムラインを見たい場合には毎回 mount(1M) を実行する必要があります。

使い方

mount -F iumfs -o user=user_name twitters_url mount_point

例)

# /usr/sbin/mount -F iumfs -o user=kaizawa2test twitterfs://twitter.com /mnt

実は上記の「twitterfs」と「twitter.com」に意味は無く実際には何か文字が入っていれば大丈夫です。(でも省略はできません)
-o オプションで twitter アカウントをしていするのを忘れないようにしてください。これは必須オプションです

Twitterファイルシステムユーザモードデーモンの起動

圧縮ファイルを展開するとその中に twitterfsd.jar デーモンプログラム が含まれています。このデーモンプログラムは twitter4j ライブラリを利用して twitter.com にアクセスします。
デーモン起動用に start-twitterfsd というシェルスクリプトを用意してありますのでこちらを使って起動してください。

$ ./start-twitterfsd

もしインターネットに接続するためにプロキシサーバを経由必要がある場合には以下のようにしてプロキシサーバを指定してください。

$ ./start-twitterfsd -Dhttp.proxyHost=192.168.1.1 -Dhttp.proxyPort=8080

タイムラインの表示

オートマウントを使用している場合には /twitter/<あなたのアカウント> に移動してください。<あなたのアカウント>はあなたの Twitter アカウントになります。このディレクトリが予め存在する必要はありません。あなたがこのディレクトリに cd コマンドでアクセスした際に automount デーモンが自動的にディレクトリを作成します。

$ cd /twitter/kaizawa2test

もしこれが始めてのアクセスであれば「setup」というファイルが見つかるはずです。

$ cd /twitter/kaizawa2test
$ ls 
./ ../ setup*

これは Twitter のアクセストークンを取得するための初期設定プログラムです。
このプログラムを実行すると以下の様なメッセージが現れます。

$./setup
Open the following URL and grant access to your account:
http://api.twitter.com/oauth/authorize?oauth_token=9zhKalThfMKB5R20C2pGjdBnc0ydWIODzeDmFS3fsNI
Enter the PIN(if aviailable) or just hit enter.[PIN]:

これは twitterfsd デーモンがあなたのタイムラインを取得したりツイートしたりするのに必要な手順になります。
setup プログラムの出力記載されたURLにアクセスしてください。以下のようなページが現れます。

f:id:kaizawa2:20140103004318p:plain

もしこのアプリを承認する場合には「連携アプリを承認」を押してください。

f:id:kaizawa2:20140103004506p:plain

表示された PID をコピーし、先ほどの setup プログラムのプロンプトに入力します。

$ ./setup
Open the following URL and grant access to your account:
http://api.twitter.com/oauth/authorize?oauth_token=9zhKalThfMKB5R20C2pGjdBnc0ydWIODzeDmFS3fsNI
Enter the PIN(if aviailable) or just hit enter.[PIN]:9194085 <= ここにPIN番号を入力します
Twitter access token setup successfully


これで twitterfsd デーモンは twitter サーバと通信可能な状態になりました。
もしここでエラーになった場合にはあなたの環境ではプロキシサーバが必要である可能性があります。
以下のようにプロキシサーバをしていして setup プログラムをやり直してみてください。

$ ./setup -Dhttp.proxyHost=192.168.1.1 -Dhttp.proxyPort=8080

setup プログラムが終了した後改めてアカウントのフォルダを見ると以下のようにみえるはずです。

f:id:kaizawa2:20140103104305p:plain

「post」以外のそれぞれのファイルはタイムラインを表しています。
また、followers ディレクトリはフォロワーのタイムラインのファイルを持っており、friends ディレクトリはフォローしている各アカウントのタイムラインのファイルを持っています。*1

home ホームタイムライン
user 自分の投稿のタイムライン
followers/<アカウント> フォロワーのタイムライン
friends/<アカウント> フォローしているアカウントのタイムライン
retweet_of_me リツイートされた自分の投稿のタイムライン

これらのタイムラインファイルを UNIX コマンドで開くことができます。
各ファイルはバックグラウンドで自動的に更新されていきます。

例) ホームタイムラインを tail(1M) コマンドでチェックする

f:id:kaizawa2:20140103094337p:plain

home タイムラインはストリームAPIを使っているためリアルタイムに近い更新が行われます。
ただし他のタイムラインについてはTwitter API の取得回数制限回避のため遅めです。*2

タイムライファイルはテキストファイルのように見せていますので cat(1M) や vi(1M) といったテキストコマンドで操作できます。

もちろん、ファイルをコピーしてもコピー先のファイルは更新されませんのであしからず。

ツイートを投稿する

通常の投稿

「post」ファイルがツイート投稿用のファイルです。ファイルの中身は常に空でサイズも0です。
このファイルに UTF-8 で文字列を書き込むことによって Twitter のツイートとして投稿できます。

f:id:kaizawa2:20140103101324p:plain

Twitter 側でこのファイル書き込みが実際に投稿されていることを確認できます。

f:id:kaizawa2:20140103101417p:plain

もしファイルに対して140時以上を書き込んだ場合には、メッセージは自動的に140文字づつに分割され投稿されます。
2番目以降の投稿には「(contd)」という接頭辞が着きます。

f:id:kaizawa2:20140103104059p:plain

リプライとしての投稿

replies フォルダにリプライを投稿したいアカウントのファイルを作成しそのファイルにメッセージを書き込んで下さい。

例) ichgia2test というアカウントに「着いたよ」というリプライを送る。

f:id:kaizawa2:20140103111944p:plain

上記のように予めファイルがある必要はありません。echo のリダイレクト先としてアカウント名をするだけでOKです。
すると、以下のように @ichgia2test という接頭辞が付いて投稿されます。

f:id:kaizawa2:20140103112138p:plain

通常のメッセージに @ichgia2test と自分で付けて投稿しても同じことですが、この方法の場合投稿の前にアカウントの存在確認を行います。もし投稿先のアカウントが存在しない場合には ENOENT が返ります。

f:id:kaizawa2:20140103112546p:plain

Tips

フォローしているアカウントへのリプライを非表示

デフォルトではホームタイムラインにはあなたがフォローしていないアカウントからフォローしているアカウントへのリプライが全て表示されます。これを止めるためには -Dtwitter4j.stream.user.repliesAll=false オプションを使って twitterfsd を起動します。

$ ./start-twitterfsd -Dtwitter4j.stream.user.repliesAll=false 

FAQ

friends、followers フォルダが空なんだけど?

デフォルトでは空です。ls コマンド等で初めてのアクセス後、バックグラウンドで friends, follower のチェックが行われて徐々に追加されていきます。Twitter APIの制限のため1分間に20アカウントづつ増えます。

friends、followers 以下のアカウントのタイムラインが全然更新されないんだけど?

Twitter API の更新回数制限のため5秒に1アカウント分のタイムラインしか更新することができません。このため、friends、follower 数の合計が多い場合には各アカウントの更新頻度は長くなります。
例) 計1,000 の follower/friends が居る場合
更新間隔 = 5 x 1,000 = 5,000 秒 = 83分 = 1時間23分

replies フォルダ空なんだけど?

デフォルトは空です。エディタやリダイレクトなどを使ってリプライを送りたいTwitterアカウント名のファイル名として指定してを作成します。
ただし、実際に存在しないアカウント名を使っても ENOENT エラーとなりファイルは作成されません。

オートマウントディレクトリに知らない名前のがあるんだけど?

オートマウントディレクトリ(例では /twitter) 以下のフォルダは automountd によって自動的に作成されます。
ご自身で明示的にアクセスしなくてもシェルなどのアプリがファイルの存在を確認するためにアクセスしようとするとその名前のフォルダが作成されてしまいます。不要だと思われる場合には管理者権限で umount(1M) してください。

sol11:/twitter $ ls
./             ../            .paths/        kaizawa2test/
sol11:/twitter $ sudo umount /twitter/.paths
sol11:/twitter $ ls        
./             ../            kaizawa2test/

既知の問題

  1. タイムラインファイルのアクセス制限が行われていません。なのでシステムにログイン可能なユーザはすべてのタイムラインが読めてしまいます。

License

Twitter Filesystem is provided under Apache License 2.0.
This program is an open source software and free of charge.
You can use this program freely for any commercial and non-commercial use.

This program uses twitter4j.

Twitter4J
Copyright (c) 2007, Yusuke Yamamoto All rights reserved.
http://twitter4j.org

*1:最初は空です。初めてアクセスを期にバックグラウンドで後徐々にファイルが増えていきます。

*2:FAQご参照ください

ESXiMonitor: ESXi モニターツール

ESXiMonitor とは?

ESXiMonitor はリモート VMware ESXi サーバ上のゲストOSの電源の状況を監視し、パワーオン、パワーオフ、リセット、シャットダウンなどの電源操作を行う行うツールです。
English page is here

※ このツールは Java で書かれており UI は JavaFX を使っています。
また、ESXi サーバの操作には VMware VI (vSphere) Java API を利用しています。

サポートされるプラットフォーム

ESXiMonitor は 100% Pure Java アプリケーションですので Java SE が実装されているプラットフォームであれば動作させることができます。

前提条件

Java のインストール

システムに JRE 7 かそれ以降のバージョンの JRE もしくは JDK がインストールされている必要があります。

ESXi サーバのサポートモードの有効化

ESXi サーバに対して電源操作を行うためには root ユーザでの SSH ログイン(通称サポートモード)を有効にする必要があります。以下のドキュメントを参考にしてください。

ダウンロードとインストール

以下のリンクをクリックするだけでこのツールを起動することができます。(*1)(*2)
Java プログラムとライブラリが自動的にダウンロードされシステムにインストールされます。

ESXiMonitor

最新のソースコードをダウンロードしたい場合には以下のリンクからダウンロードできます。

esximonitor.tar.gz

コードは Github にあげているので Github 上でコードを見ていただくこともできます。

https://github.com/kaizawa/esximonitor

(*1) Mac OS の場合には esximonitor.jnlp というファイルがダウンロードされるだけで直接起動できないようです。起動するためには Finder からダンロードフォルダを開き esximonitor.jnlp ファイルを右クリックして「このアプリケーションで開く -> Java Web Start.app(デフォルト)」で起動してください。
(だれかブラウザから直接起動できる方法ご存知の方教えてください!!)

(*2) jar ファイルを自著署名しているので起動時に以下のような警告がでます。

もし問題無いと判断されましたら「リスクを受け入れて、このアプリケーションを実行します」にチェックをいれて「実行」ボタンを押してください。
将来的にはこの方法でも起動ができなくなるようなのでコード証明書の取得も検討中です。(高い…)

コンパイル方法

上記のソースコードをダウンロードしてきた場合には圧縮ファイルを展開したディレクトリで ant コマンドを実行することでコンパイルすることができます。

$ ant
Buildfile: /Users/ka78231/NetBeansProjects/esximonitor/build.xml
 :
default:

BUILD SUCCESSFUL
Total time: 7 seconds
$

使い方

ESXiMonitor の起動

ブラウザーからの起動

先述のとおり以下のリンクをクリックするだけで起動できます。

ESXiMonitor

コマンドラインからの起動

ソースコードからビルドして jar ファイルを作成した場合、インターネットに接続せずに直接起動することもできます。
上記のソースコード圧縮ファイルの中に UNIX、Windows 向けの起動スクリプトが用意してありますのでそちらを使って起動してください。

$ cd kaizawa-esximonitor-69021cf
$ ./start.sh
  • Windows
C:\> cd kaizawa-esximonitor-69021cf
C:\kaizawa-esximonitor-69021cf> start.bat

ESXi サーバの指定

初回起動時以下のような画面が現れ ESXi サーバの情報が求められます。

ホスト名(もしくはIP)、ユーザ名、パスワードを入力し最後に「Add」ボタンを押してください。
正常にログインできれば ESXi サーバ上の VM のリストが現れるはずです。

ゲストOSの監視と電源操作

ESXi サーバが指定されると、以下のようなウィンドウが現れます。

豆電球のアイコンは各ゲストの電源のオンオフを表しています。
その左側には、パワーオン、パワーオフ、リセット、シャットダウンのそれぞれの操作に対応したボタンが表示されています。

複数の ESXi サーバの監視

もし複数台の ESXi サーバの監視を行いたい場合には、メニューから Edit -> ESXi Servers をたどってください。
するとサーバの一覧画面が現れるはずです。「New」ボタンを押すと初回起動時と同様に ESXi サーバの情報を入力する画面が表示されます。
なお、ESXiMonitor ツールはこれらの情報を覚えていますので、次回起動時に再度サーバ情報を入力する必要はありません。

その後、監視したい方のサーバをコンボボックスから選択肢ます。

※ コンボボックスの右横の「Show All Servers」にチェックをいれると全てのサーバを一度に監視できます。

License

ESXiMonitor is provided under BSD License.
This program is an open source software and free of charge.
You can use this program freely for any commercial and non-commercial use.


This tool is utilizing following libraries and icons.

VMware VI (vSphere) Java API
http://vijava.sourceforge.net

Ganymed SSH-2 for Java
http://www.ganymed.ethz.ch/ssh2/

FAMFAMFAM - Silk Icons.
http://www.famfamfam.com/lab/icons/silk/






Mac OS X 環境で NetBeans 7.4 + JDK7u45 が頻繁にハングする

タイトルの通り、以下の環境にて NetBeans がハングし頻繁に操作不能に陥っていました。

調べたところどうやら以下の Mac OS X 向けの Java の Bug に該当するようです。

JDK-8025588 : macosx Frozen AppKit thread in 7u40

事象の確認方法としては jstat コマンドなどでハングしたときの Netbeans のスレッドダンプを確認すると以下の様なダンプが見られるとのことです。

$ /usr/bin/jstack -F 69760
  ...
Thread 1287: (state = IN_NATIVE)
 - sun.lwawt.macosx.LWCToolkit.doAWTRunLoop(long, boolean) @bci=0 (Interpreted frame)
 - sun.lwawt.macosx.LWCToolkit.invokeAndWait(java.lang.Runnable, java.awt.Component) @bci=67, line=549 (Interpreted frame)
 - sun.lwawt.macosx.LWCToolkit.invokeAndWait(java.util.concurrent.Callable, java.awt.Component) @bci=11, line=489 (Interpreted frame)
 - sun.lwawt.macosx.CAccessibility.invokeAndWait(java.util.concurrent.Callable, java.awt.Component) @bci=2, line=75 (Interpreted frame)
 - sun.lwawt.macosx.CAccessibility.getFocusOwner(java.awt.Component) @bci=8, line=521 (Interpreted frame)
   ...

(* 69760 は NetBeans のPIDです.)
この Bug レポートによると現時点 (JDK7u45) では以下の回避策があるようです。

  1. JDK7u25 を使う
  2. JDK8 Early Access を使う

以下、念のためそれぞれの実施方法です。

回避策1: JDK7u25 を使う

現在JDK7u45(もしくはu40)を使っているという前提です。

JDK7u45をアンインストール

/Library/Java/JavaVirtualMachines ディレクトリに移動し JDK7u45(もしくはu40)を削除

$ cd /Library/Java/JavaVirtualMachines
$ rm -rf jdk1.7.0_45.jdk

参考: Uninstalling the JDK

JDK7u25のダウンロード

Java SE Development Kit 7u25

回避策2: JDK8 Early Access を使う

JDK8のEarly アクセスのダウンロード

JDK™ 8 Early Access Releases

私は JDK8 build b120 を使っていてこれまでのところ問題なく動いています。

追記

JDK7u25を使う対策についてですが、MacBook Pro の Retina ディスプレイを使っている場合、JDK7u25ではRetinaに対応していないので表示がぼんやりとしてと悲しい状態になります。

JDK7u45の場合

f:id:kaizawa2:20131222122749p:plain

JDK7u25の場合

f:id:kaizawa2:20131222122738p:plain

heapstat: C-heap の利用状況確認

heapstat コマンドとは?

heapstat コマンドは動作中のプロセスや core ファイルから、アプリケーションの C-heap 領域の利用状況を表示するコマンドです。

アプリケーションが malloc(3C) を使ってメモリを確保した場合アプリの必要に応じて C-heap 領域が拡張します。しかしながら、free(3C)を呼び出しても実際にはこのメモリブロックはシステムに返却されません。フリーされたメモリブロックはフリーリストの置かれ、その後のアプリからの確保要求のために保持されます。

このコマンドは C-heap 内の空きメモリサイズを表示するツールになります。

使い方

heapstat [-v] { pid | core } [interval]

プロセスID(PID)を引数に与えることで heapstat コマンドは動作中のプロセスの C-heap 領域の利用状況を表示します。

$ heapstat 14641    
   free tree    next free   small free  bottom size    heap size    free size   free%
    size(kb)     size(kb)     size(kb)         (kd)         (kb)         (kb)     (%)
          16            0           17            3         6656           36       0 

もし引数としてインターバルも指定した場合、heapstat コマンドは指定した間隔(秒)毎に利用状況を出力します。

$ heapstat 14680 2
   free tree    next free   small free  bottom size    heap size    free size   free%
    size(kb)     size(kb)     size(kb)         (kd)         (kb)         (kb)     (%)
         327            0           67            0         8192          395       4 
         327            0           67            0         8192          395       4 
         327            0           66          227         8192          621       7 
         327            0           66          227         8192          621       7 
         327            0           66          227         8192          621       7 
         327            0           66          227         8192          621       7 

もし PID の代わりに core ファイルのファイル名を指定した場合は core ファイルに含まれる C-heap 領域の使用状況を出力します。

$ heapstat /var/tmp/core.974                                                                   
   free tree    next free   small free  bottom size    heap size    free size   free%
    size(kb)     size(kb)     size(kb)         (kd)         (kb)         (kb)     (%)
           0            1        51199        52851       129856       104051      80 

出力項目

free tree size

Solarismalloc は Self-Adjusting Binary Search Trees を使ってフリーされたメモリブロックを管理しています。
この列はこのバイナリツリー配下のフリーサイズの合計値を報告します。

next free size

free(3C)が呼ばれたタイミングではフリーされたブロックはフリーリストには追加されません。それらは「Last Freed」といフラグがつけられ、次回の malloc(3C)呼び出しまで保持されます。もし次回の malloc(3C) での要求サイズがこの保持したブロックのサイズに合致するなら、それがそのままアプリに返却されますが、もし要求に合わない場合にはフリーリストに追加されます。
この列は最後にフリーされ保持されているメモリブロックのサイズを表示します。

small free size

もし malloc(3C) が 64バイト以下のサイズのメモリを要求された場合、malloc(3C) は小さなサイズで C-heap を拡張する事無く、連続した小さなブロックを一度に確保し、後でリクエストされるまで保持します。(128個)
この列はこの未仕様の小さなブロックの合計サイズを表示します。

bottom size

この列はC-heap領域のの最後のフリーな塊のサイズを表示します。

heap size

この列は現在のアプリケーションのC-heap領域のサイズを表示します。

free size

この列はC-heap 領域内の空きサイズを表示します。

= free tree size + next free size + small free size + bottom size

free%

この列はC-heap領域内のの空きサイズの比率を表示します。

ダウンロード

以下のリンクからソースコードのアーカイブをダウンロードできます。
heapstat-master.zip

またコードは github で参照することもできます。

https://github.com/kaizawa/heapstat

コンパイル方法

./configure、make にてコンパイルします。
インストールする際には root 権限のあるアカウントで make install を実行します。

$ ./configure                                                       
checking for gcc... gcc
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables... 
checking whether we are cross compiling... no
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether gcc accepts -g... yes
checking for gcc option to accept ISO C89... none needed
checking for g++... g++
checking whether we are using the GNU C++ compiler... yes
checking whether g++ accepts -g... yes
checking for a BSD-compatible install... /usr/bin/ginstall -c
checking for isainfo... yes
configure: creating ./config.status
config.status: creating Makefile
config.status: creating heapstat-test/Makefile
$ make
gcc -g -I. -lproc -DPACKAGE_NAME=\"heapstat\" -DPACKAGE_TARNAME=\"heapstat\" -DPACKAGE_VERSION=\"0.1.0\" -DPACKAGE_STRING=\"heapstat\ 0.1.0\" -DPACKAGE_BUGREPORT=\"admin2@whiteboard.ne.jp\" -DPACKAGE_URL=\"\" -W -Wall -Wno-unknown-pragmas -Iinc -m64 -o heapstat heapstat_main.c
gcc -g -I. -lproc -DPACKAGE_NAME=\"heapstat\" -DPACKAGE_TARNAME=\"heapstat\" -DPACKAGE_VERSION=\"0.1.0\" -DPACKAGE_STRING=\"heapstat\ 0.1.0\" -DPACKAGE_BUGREPORT=\"admin2@whiteboard.ne.jp\" -DPACKAGE_URL=\"\" -W -Wall -Wno-unknown-pragmas -Iinc -m32 -o heapstat32 heapstat.c
gcc -g -I. -lproc -DPACKAGE_NAME=\"heapstat\" -DPACKAGE_TARNAME=\"heapstat\" -DPACKAGE_VERSION=\"0.1.0\" -DPACKAGE_STRING=\"heapstat\ 0.1.0\" -DPACKAGE_BUGREPORT=\"admin2@whiteboard.ne.jp\" -DPACKAGE_URL=\"\" -W -Wall -Wno-unknown-pragmas -Iinc -m64 -o heapstat64 heapstat.c
$ sudo make install 
/usr/bin/ginstall -c -d -m 0755 -o root -g bin /usr/local/bin
/usr/bin/ginstall -c -m 0755 -o root -g bin heapstat /usr/local/bin
/usr/bin/ginstall -c -m 0755 -o root -g bin heapstat32 /usr/local/bin
/usr/bin/ginstall -c -m 0755 -o root -g bin heapstat64 /usr/local/bin

テストした Solaris バージョン

このコマンドは Illumos(OpenSolairs)の libc malloc(3C) のコードを元に書いています。
現在のところ Oracle Solaris 11.1 でも動作することは確認できていますが、今後 Solarismalloc(3C)の実装が変わった場合には、このコマンドが正しく動作しなくなる可能性があります。

License

This program is provided under CDDL License.