2012年3月21日水曜日

連載「libvirt を利用して日々の作業を効率化」第4回

こんにちは、OSSテクノロジーセンターの原です。

連載4回目になりました。

今回は、ゲストマシンの画面を Web から Java Applet の VNC で表示して操作出来るようにしてみましょう。

最近では Japa Applet 以外にも、HTML5 や JavaScript、Flash などでも VNC 接続が出来るようなって来ました。

興味がある方は Java Applet 以外の実装の VNC の利用も検討してみてもよろしいかと存じます。

特に HTML5 での接続ができれば、スマートフォンやタブレットデバイスでの接続など夢が広がりますね・・・!

また、VNC 以外で接続する方法として SPICE や、仮想シリアルコンソールを接続して直にコンソールを取得することも可能です。

シリアルコンソールの場合、ゲストマシン内での設定も必要になりますが、 AjaxTerm などで直接繋ぐことができます。


さて、話が逸れたので本題に戻します。

まずは、Java Applet の VNC のバイナリを入手しましょう。

tightvnc-1.3.10_javabin.tar.gz をダウンロードして、webvirt の public/applet ディレクトリ配下に展開します。

# cd webvirt
# mkdir -p public/applet
# mv /path/to/file/tightvnc-1.3.10_javabin.tar.gz public/applet
# cd public/applet/
# tar zxvf tightvnc-1.3.10_javabin.tar.gz

配置できたら、VNC 用の画面を実装して行きましょう。

URI はとりあえず /vnc とでもしましょうか。


webvirt.rb
#!/usr/bin/ruby

require 'rubygems'
require 'sinatra'
require 'libvirt'
require 'rexml/document'

get '/' do
    conn = Libvirt::open("qemu+ssh://root@kvmhost/system")
    @act_vm_list = conn.list_domains
    @act_vm_list_name = []
    unless @act_vm_list.nil?
      act_vms = @act_vm_list
      act_vms.each do |act_vm|
        act_vm = act_vm.to_i
        vm = conn.lookup_domain_by_id(act_vm)
        @act_vm_list_name << vm.name
      end
    end
    @sby_vm_list = conn.list_defined_domains
    erb :index
end

get '/vnc' do
  conn = Libvirt::open("qemu+ssh://root@kvmhost/system")
  vm = conn.lookup_domain_by_name(params[:name])

  vm_source = vm.xml_desc
  vm.free

  vm_doc = REXML::Document.new vm_source
  
  # show guest xml
  # vm_doc.write(STDOUT)
  
  vm_doc.elements.each("/domain/devices/graphics[@type='vnc']/"){|address|
    @vm_port   =  address.attributes["port"]
    @vm_passwd =  address.attributes["passwd"]
    @vm_keymap =  address.attributes["keymap"]
    @vm_listen =  address.attributes["listen"]
  }

  erb :vnc
end

post '/vm_start' do
  conn = Libvirt::open("qemu+ssh://root@kvmhost/system")
  vm = conn.lookup_domain_by_name(params[:vm_name])
  vm.create
  redirect '/'
end

post '/vm_shutdown' do
  conn = Libvirt::open("qemu+ssh://root@kvmhost/system")
  vm = conn.lookup_domain_by_name(params[:vm_name])
  vm.shutdown
  redirect '/'
end

views/index.erb
<b>My KVM VMs</b>
<hr>
Active VMs<br>
<form method="POST" action="/vm_shutdown">
  <% for act_vm in @act_vm_list_name %>
    <input type="radio" name="vm_name" value="<%= act_vm %>"/> <%= act_vm %> |
    <a href="Javascript:w=window.open('/vnc?name=<%= act_vm %>','VNC','Width=690,Height=500');w.focus();">VNC</a>
    <br>
  <% end %>
  <input type="submit" name="Submit" value="shutdown">
</form>

<hr>
Standby VMs<br>
<form method="POST" action="/vm_start">
  <% for sby_vm in @sby_vm_list %>
    <input type="radio" name="vm_name" value="<%= sby_vm %>"/> <%= sby_vm %><br>
  <% end %>
  <input type="submit" name="Submit" value="start">
</form>

views/vnc.erb
<applet code="VncViewer.class" archive="/applet/classes/VncViewer.jar" width="670" height="480">
  <param name="host" value="192.168.86.40">
  <param name="port" value="<%= @vm_port %>">
  <param name="encoding" value="hextile">
</applet>

webvirt.rb でまず、get メソッドでデータを受け取った時の振る舞いを記述します。

ちょっとややこしいですが、get メソッドで名前を受け取って、それを元に lookup_domain_by_name メソッドでゲストマシンのオブジェクトを取得します。

ゲストマシンのオブジェクトは XML 形式になっているので、そこから elements.each("/domain/devices/graphics[@type='vnc']/") で VNC 接続に関する部分を取得します。

vm_doc.write(STDOUT) というおまじないがコメントアウトしてありますが、このコメントアウトを外すと WEBrick のコンソールに XML 定義が流れます。一度は確認することをお勧めします。

と言っても、内容的には virsh dumpxml <guest name> で取れるものと同じになります。

ゲストマシンのオブジェクトが XML 形式になっている事や、それを一回展開しないとイケナイのがややこしいですが、原理がわかってしまえば直感的に扱えるはずです。


次に、views/index.erb です。

今時ポップアップはどうだろう・・・とか色々ありますが、今回は習作という事で JavaScript でポップアップさせるようにしました。

'/vnc?name=<%= act_vm %>' で直接ゲストマシンのお名前を送り込み、コントローラ側で妥当性の判定もしないという、セキュリティをちょっとでもかじったことがある人なら顔を顰めるような挑戦的な実装になっています。

実際に作成するときは重々気をつけてください。


最後に views/vnc.erb です。

このファイルではあまりごちゃごちゃすることがありません。

単純に少しの変数とパスのとおりに Applet を呼び出しているだけです。

どうでしょう、実装ができたらブラウザから確認してみてください。

ゲストマシンの画面がとれていれば成功です。

もし画面がとれない場合、ゲストマシンの Graphics の設定を確認してください。

LISTEN ADDRESS が 127.0.0.1 のみになっている場合があります。全てのアドレスから接続を受け付ける場合は 0.0.0.0 に設定します。

また、クライアント側の Java のセキュリティが設定されている場合、java.security.AccessControlException が表示され、VNC の画面を表示できないことがあります。

その場合、java.policy の grant の設定を見直してみてください。


いかがでしたでしょうか。

ゲストマシンの画面が取れるようになると、だいぶ "それらしく" なりますね。

次回もお楽しみに。

ではでは。

0 件のコメント:

コメントを投稿