Hatena::ブログ(Diary)

かーねる・う゛いえむにっき このページをアンテナに追加 RSSフィード Twitter

2014-12-18

[][]mrubyでUEFIブートローダなやつのバイナリ配布

多分みなさんEDK2のビルドが面倒い最高で試すの嫌だと思うので、バイナリ配布しておきますね。

こちらです:

https://s3.amazonaws.com/syuu1/mruby.efi

ブートローダスクリプトはこちら:

https://raw.githubusercontent.com/syuu1228/mruby_on_efi_shell/devel/example/bootloader.rb

いきなりefibootmgrなどを使ってブートメニューに組み込もうとせずに、EFI Shellから起動することを強くおすすめします(しくってブート出来なくなったりすると困ると思うので)。

※追記:落としてバイナリ見れば分かると思うんだけど、X64版です。他のアーキテクチャ(IA32を含む)では動かんです。

[][]GCEでOSvを動かそう

OSvは、ハイパーバイザやIaaSプラットフォームアプリケーションデプロイすることに特化した軽量OSです(※詳しくはこちら)。

今回はRuby on Railsで書かれたブログエンジンPublify」をインストールしたOSvのイメージGoogle Compute EngineGCE)へデプロイしてみます。

プロジェクトを作成

まずOSv用にプロジェクトを作成してみます。

f:id:syuu1228:20141218083122p:image

Cloud Storageの登録

Cloud Storageが有効化されてなかったので登録しておきます。

f:id:syuu1228:20141218083123p:image

GCE SDKインストール

GCE管理用のコマンド群を使用するため、SDKインストールします。

curl https://sdk.cloud.google.com | bash
プロジェクトIDの設定

先ほど作成したプロジェクトログインしてIDを設定します。

gcloud auth login
gcloud config set project fresh-mason-798
バケットの作成

OSvのディスクイメージアップロードするためにCloud Storageへバケットを作成します。

gsutil mb gs://osv_test
Rubyのインストール

Publify入りのディスクイメージビルドするためにrvmでRubyインストールします。

 gpg --keyserver hkp://keys.gnupg.net --recv-keys D39DC0E3
\curl -sSL https://get.rvm.io | bash -s stable
source ~/.profile
rvm install 2.1.4
rvm use 2.1.4
sudo yum install sqlite-devel
OSvイメージビルド

OSvのソースコードをcloneしてPublifyのイメージビルドします。

ビルドが終わったらGCE用のtar.gzファイルを生成します。

git clone https://github.com/cloudius-systems/osv.git
git submodule update --init --recursive
make image=ruby-publify
./scripts/gen-gce-tarball.sh

※今回は、make image=ruby-publify,httpserverとしたところエラーが出て起動しなかった(Rubyとの組み合わせで発生するバグ)のでhttpserverを外しましたが、これがないと8000番ポートRESTサーバが起動しないのでOSvの管理が著しく不便になります。Ruby以外のアプリを使用する時は必ず付けることをオススメします。

OSvイメージデプロイ

gsutil・gcutilコマンドを使ってgen-gce-tarball.shコマンドで生成したosv.tar.gzをアップロードデプロイします。

今回はus-central1-aのf1-microへosvという名前で実行することにしました。

gsutil cp build/release/osv.tar.gz gs://osv_test/osv.tar.gz
gcutil addimage osv gs://osv_test/osv.tar.gz
gcutil addinstance --image=osv  --machine_type=f1-micro  --zone=us-central1-a osv
動作状況の確認

getserialportoutputでOSvインスタンスの動作状況を確認します。

gcutil getserialportoutput osv

以下のような出力が表示されます。

(略)
OSv v0.16-22-ga6c87f0
eth0: 10.240.159.233
sigaltstack() stubbed
=> Booting Thin
=> Rails 3.2.21 application starting in production on http://0.0.0.0:3000
=> Call with -d to detach
=> Ctrl-C to shutdown server
You did not specify how you would like Rails to report deprecation notices for your production environment, please set config.active_support.deprecation to :notify at config/environments/production.rb
WARNING: fcntl(F_SETLK) stubbed
Thin web server (v1.6.3 codename Protein Powder)
Maximum connections set to 1024
Listening on 0.0.0.0:3000, CTRL+C to stop
ファイヤウォールの設定

グローバルIPからポート3000へ接続出来るように設定を変更します。

gcutil addfirewall publify --description="publify http" --allowed="tcp:3000"
Publifyへ接続

http://<インスタンスグローバルIP>:3000/ へブラウザから接続すると、Publifyのセットアップ画面が出てくるので設定を開始します。

f:id:syuu1228:20141218083124p:image

覗いてみる

http://146.148.55.173:3000/ で起動中です(そのうち落とします)。


まとめ

OSvのイメージGCEデプロイする方法を解説しました。

今回はPublifyの例を挙げましたが、CassandraのようなJavaアプリRedisのようなネイティブアプリも同様の手順でデプロイすることが可能です。

2014-12-12

[][]UEFI Native API on mruby

mrubyでブートローダを書けるようにしました - かーねる・う゛いえむにっき記事はlibc API経由でmrubyからUEFI機能を使う話でした。

これとは別に、mruby on efi shellにはUEFIネイティブAPIアクセスするためのクラスが用意されているという話をします。

mruby on efi shellのソースコードを眺めていると、二つのスクリプトが見つかります(こちら)。

一つはdump_cmos.rbで、UEFI::LowLevel.io_read8/write8を呼び出してIOポートアクセスを行っている事が分かります。

################################################3
# Example
#  CMOS dump.

class String
  def rjust(width, padding=' ')
    str = ""
    if (self.length < width)
      str = padding * (width - self.length)
    end
    return str + self
  end
end

CMOS_INDEX = 0x70
CMOS_DATA = 0x71

orig_index = UEFI::LowLevel.io_read8(CMOS_INDEX)
(0..0x7F).to_a.each_with_index do |i, index|
  UEFI::LowLevel.io_write8(CMOS_INDEX, i)
  v = UEFI::LowLevel.io_read8(CMOS_DATA)
  print "#{v.to_s(16).rjust(2, '0')} "
  print "\n" if (index % 16 == 15)
end
UEFI::LowLevel.io_write8(CMOS_INDEX, orig_index)

もう一つがread_disk.rbで、こちらはUEFI::ProtocolやUEFI::BootService、UEFI::Guidなどのクラスを用いてEFI_DISK_IO_PROTOCOLへアクセスし、メディア情報を取得しています。

# Sample of BLOCK_IO_PROTOCOL

def print_binary_data(data)
  data.bytes.each_slice(16) do |data|
    # print hex.
    data.each do |i|
      print i.to_s(16).rjust(2, "0").upcase
      print " "
    end
    print "   "
    # print ascii.
    data.each do |i|
      print (0x20 <= i && i <= 0x7F) ? i.chr : "."
    end
    puts ""
  end
  puts ""
end

class BlockIoProtocol < UEFI::Protocol
  GUID = UEFI::Guid.new("964e5b21-6459-11d2-8e39-00a0c969723b")

  define_variable(:revision, :u64)
  define_variable(:media, :p)
  define_function(:reset, :e, [:p, :b])
  define_function(:read_blocks, :e, [:p, :u32, :u64, :u64, :p])
  #...
end

# I will rename UEFI::Protocol to UEFI::Structure or any other name...
class Media < UEFI::Protocol
  define_variable(:media_id, :uint32)
  #...
end

# See http://wiki.phoenix.com/wiki/index.php/EFI_DISK_IO_PROTOCOL
class DiskIoProtocol < UEFI::Protocol
  GUID = UEFI::Guid.new("CE345171-BA0B-11d2-8e4F-00a0c969723b")

  define_variable(:revision, :u64)
  define_function(:read_disk, :efi_status, [:p, :u32, :u64, :u64, :p])
  #...
end


# Find first disk.
handles = UEFI::BootService.locate_handle_buffer(BlockIoProtocol::GUID)
handle = handles.first
puts "handle: #{handle}"

# Locate BlockIoProtocol to get the pointer of media.
ptr = UEFI::BootService.handle_protocol(handle, BlockIoProtocol::GUID)
bp = BlockIoProtocol.new(ptr)
# Get media.
media = Media.new(bp.media)
puts "media_id: #{media.media_id}"

# Find DiskIoProtocol related to bp.
ptr = UEFI::BootService.handle_protocol(handle, DiskIoProtocol::GUID)
dp = DiskIoProtocol.new(ptr)

buf = " " * 512  # Buffer to be filled with returned data.
st = dp.read_disk(ptr, media.media_id, 0, buf.size, buf)
if (st.success?)
  print_binary_data(buf)
else
  puts "ERROR: #{st}"
end

プロトコルハンドルを作るところの操作クラス継承してインタフェースを動的に定義するなどの違いはありますが、概ねEDK2に含まれるCで書かれたUEFIのサンプルアプリと同様のインタフェースを持っている事が分かります。

C拡張コードmruby_on_efi_shell/src at master ? masamitsu-murase/mruby_on_efi_shell ? GitHub)を眺めてみると、UEFI::Handle, UEFI::Pointer, UEFI::Protocol, UEFI::Status, UEFI::BootService, UEFI::LowLevel, UEFI::RuntimeServiceのようなクラス定義されています。

このインタフェースもEDK2で使われているものと大体一致しているので、完全ではないかもしれないがCのコード翻訳していけばだいたいmrubyで記述出来そうだと予想されます。

今回はここまでで、次回何らかのサンプルコードを書いてみることにします。

[][]mrubyでブートローダを書けるようにしました

カーネルVM@関西7回目でお話したネタです。スライドはこちら:

既にmrubyはUEFI移植されています(覚え書き: mruby on EFI Shell)。

また、EFI StubカーネルUEFIアプリから実行するのは非常に簡単であることが分かっています(実際にソースコードを書いて実験しました)。

以上のことから、「mrubyにEFI Stubカーネルファイル名を指定したら実行するAPIを足せば、mrubyでブートローダ実装可能になるに違いない」と考えました。

ただ面白いというだけでなく、mruby-socket・mruby-simplehttp-socketなどのmrbgemsを移植していけばブート処理を記述したmrubyスクリプトカーネルのものHTTPから取得することが出来るようになり、簡単なスクリプトで高度にカスタマイズされたブート処理をOS依存せず行う事が出来るようになると思い、まずはたたき台として単純なブートメニューを実現するものを実装してみました。

以下に今回実装したブートローダのmrubyスクリプトを貼ります(Dirクラスのコードは一部省略しました。フルサイズのコードはこちら)。

base_dir = "kernels"
kernel_options = "root=/dev/sda3"
kernels = []

Dir.foreach(base_dir) do |ent|
	next if ent == "." || ent == ".."
	kernels << ent
end

puts "** Boot Menu **"
(0...kernels.length).each do |i|
	puts "[#{i}] #{kernels[i]}"
end
loop do
		puts "Input number:"
		key = Shell.gets.chomp
		idx = key.to_i
		if kernels.length <= idx
			next
		end
		puts "#{base_dir}\\#{kernels[idx]} #{kernel_options}"
		Shell.exec "#{base_dir}\\#{kernels[idx]} #{kernel_options}"
		break
end

ここでは、ブートローダを実現するために「Dir」と「Shell」クラス実装しています。

(mruby_on_efi_shellでは殆どのモジュールが無効化されている上にmrbgemsのビルドが封じられており、更にLinuxと完全に同等のAPI提供してはいないので、ビルドスクリプト編集したりAPIを叩いて1クラスごとに定義していく必要がありました。)

Dirクラスはmruby-dirから最低限必要コードを抜き出してきたものです。

mruby_on_efi_shell/dir.c at devel ? syuu1228/mruby_on_efi_shell ? GitHub

普通にreaddir()などが使えているのはEDK2のlibcを使用しているからです。今回の作業では基本的にlibcのAPIを使用しています。

(このlibcは実験版扱いで、UEFIアプリケーション正式APIではなく実験的な実装です。アプリUEFIポーティングする際には便利で、mruby_on_efi_shellもこれを利用して移植されています。よって、今回はこれに頼ることにしました。)

Shellクラスは今回独自実装したもので、libc関数であるsystem(3)・fgets(3)の単純なラッパ関数(それぞれShell.exec, Shell.gets)を提供します。

以下にコードを示します。

#include "mruby.h"
#include "mruby/class.h"
#include "mruby/data.h"
#include "mruby/string.h"
#include "error.h"
#include <stdlib.h>
#include <stdio.h>
#include "uefi.h"

mrb_value
mrb_shell_exec(mrb_state *mrb, mrb_value klass)
{
  mrb_value command;
  char *ccommand;
  int ret;

  mrb_get_args(mrb, "S", &command);
  ccommand = mrb_string_value_cstr(mrb, &command);
  ret = system(ccommand);
  return mrb_fixnum_value(ret);
}

mrb_value
mrb_shell_gets(mrb_state *mrb, mrb_value klass)
{
  char buf[65535];
  if (fgets(buf, 65535, stdin) == NULL)
	return mrb_nil_value();
  return mrb_str_new_cstr(mrb, buf);
}

void
mrb_init_shell(mrb_state *mrb)
{
  struct RClass *shell;

  shell = mrb_define_class(mrb, "Shell", mrb->object_class);
  MRB_SET_INSTANCE_TT(shell, MRB_TT_DATA);
  mrb_define_class_method(mrb, shell, "exec", mrb_shell_exec, ARGS_REQ(1));
  mrb_define_class_method(mrb, shell, "gets", mrb_shell_gets, ARGS_NONE());
}

本当はUEFIネイティブAPIを使うともう少し複雑なAPIコール必要になるのですが、ここではlibc関数でその部分をラップすることで関数コール1つだけの単純なコードになっています。

上述のmrubyスクリプトの通り、fgets(stdin)でキーボード入力を取って起動するカーネルファイル名を決め、system()にカーネルファイル名と引数を渡して実行することでカーネルが起動しています。

というわけで、実に簡単なハックでブートローダがmrubyで書けるようになりました。

これだけではAPIが足りませんし、コードが極めてとっちらかっているので、もう少し整理していきたいと考えています。

現状のコードはこちらで公開しています:

syuu1228/mruby_on_efi_shell at devel ? GitHub

※このコードではEFI StubカーネルLinux)や各種UEFIブートローダ拡張子.EFIのPEバイナリ)のみがブート対象です。それ以外のものは例えUEFI対応カーネルでもブートできません。 多くのディストリビューションEFI Stubカーネル提供しているのでこれで必要充分だろうと判断しました。

[]OSvのビルド環境の構築

OSvはMacWindows上のVirtualBoxVMwareVMware ESXiなど様々な環境で動作するのですが、ビルド環境Fedoraの最新版という極めて限られた環境でのみテストされています(理屈の上では他のディストリビューションでもビルド可能ですが、細かなパッケージの差異などのためにエラーを起こすことがあります)。

より多くの環境ビルド出来るようにビルドスクリプトを手直しするのが本筋なのですが、ここでは取り敢えずFedora環境を用意して開発を始めることを目的とします。

VMware上での開発

WindowsMacではこの方法を選択する必要があります。

Linux環境でもこの方法が使用出来ますが、後述のchroot環境を使った方が高いパフォーマンスが得られます。

VMwareインストールしてFedoraディスクイメージを起動します。

(ネステッドVMを使用するため、VMwareでなくてはなりません。VirtualBoxなどでは動作しません。)

VMイメージはここで配布しています。

ユーザ名とパスワードroot:osvdev, osvdev:osvdevです。

osvdevユーザログインを行いTerminalを立ち上げ、以下のコマンドを実行することにより開発作業を始められます。

cd osv
git pull
make -j4
./scripts/run.py -n

chroot環境での開発

Fedora以外のLinuxでの開発にはこの方法おすすめです。

chrootディレクトリFedoraインストールします。

syuu1228/fedora-chroot-image ? GitHubにてchrootイメージを配布しているのでこちらを使用します。

wget -O fedora-chroot.tar.xz "https://github.com/syuu1228/fedora-chroot-image/blob/master/fedora-chroot.tar.xz?raw=true"
sudo tar -xpf fedora-chroot.tar.xz
sudo ./run-fedora.sh

chrootディレクトリ内でrootユーザシェルが実行されます。

cd ~
./bootstrap.sh

最小限必要なパッケージをインストールします。

yum install git
git clone https://github.com/cloudius-systems/osv.git
cd osv
./scripts/setup.py
git submodule update --recursive --init
make -j4
./scripts/run.py -n

OSvのソースコードダウンロードしてビルドを行います。

[][]1コマンドJavaアプリをOSvへデプロイ

OSvは、ハイパーバイザやIaaSプラットフォームアプリケーションデプロイすることに特化した軽量OSです(※詳しくはこちら)。

また、CapstanはGo言語実装されたOSvのデプロイツールです(※詳しくはこちら)。

OSvはJVM上で動くアプリケーションをメインターゲットとして開発が進められており、様々なJavaアプリケーションが動作します。

この記事ではCapstanの開発版を使って1コマンド任意JavaアプリからOSvのディスクイメージを生成&実行してみます。

Goインストール

sudo yum install go
export GOPATH=~/go

コマンドラインFedoraの例です。ディストリごとにパッケージマネージャコマンドは異なります。

Capstan開発版をインストール

go get github.com/cloudius-systems/capstan
go install github.com/cloudius-systems/capstan

これで~/go/bin/にCapstanがインストールされました。

PATH指定を省略してCapstanを呼びたい場合は~/go/binにPATHを通して下さい。

その際、既にバイナリ配布版のCapstanをインストールしている場合PATHが異なることに注意して下さい(バイナリ配布版は~/bin)。

Rhinoを試す

まずは単一jarで配布されているRhino Javascriptインタプリタを試します。

wget ftp://ftp.mozilla.org/pub/mozilla.org/js/rhino1_7R2.zip
unzip -x rhino1_7R2.zip
~/go/bin/capstan run rhino1_7R2/js.jar

capstan run <jarファイル> によってOSv+JVMディスクイメージjarファイルコピーされ、KVM上で実行されます。

実行が開始されると、OSvの起動メッセージの後にRhinoが起動してくるのでJavaScriptで遊んでみます。

Downloading cloudius/osv-openjdk/index.yaml...
160 B / 160 B [=====================================================] 100.00 % 0
Downloading cloudius/osv-openjdk/osv-openjdk.qemu.gz...
74.34 MB / 74.34 MB [===========================================] 100.00 % 3m16s
Building js...
Uploading files...
1 / 1 [=============================================================] 100.00 % 0Created instance: js
OSv v0.16
eth0: 192.168.122.15
Rhino 1.7 release 2 2009 03 22
js> print("Hello World");
Hello World
js> 1 + 2;
3
js> quit();

複数jarファイルからなるプログラムを実行してみる

One-JARを使うと複数jarファイルを一つにまとめることが出来るようなので、これを使ってApache FtpServerを一つのjarにまとめてOSvへデプロイしてみます。

まずFtpServerとOne-JARダウンロードしてきて解凍します。

wget http://ftp.riken.jp/net/apache/mina/ftpserver/1.0.6/dist/ftpserver-1.0.6.zip
unzip -x ftpserver-1.0.6.zip
cd apache-ftpserver-1.0.6
wget http://jaist.dl.sourceforge.net/project/one-jar/one-jar/one-jar-0.97/one-jar-boot-0.97.jar

まとめたjarファイルを作るための一時ディレクトリを./rootに作成し、ここのjarファイルコピーします。

mkdir -p root/main
mkdir -p root/lib
cp common/lib/*.jar root/lib
mv root/lib/ftpserver-core-1.0.6.jar root/main/
cd root
jar xvf ../one-jar-boot-0.97.jar
boot-manifest.mf

boot-manifest.mfにOne-Jar-Main-Classの行を足します。

Manifest-Version: 1.0
Main-Class: com.simontuffs.onejar.Boot
One-Jar-Main-Class: org.apache.ftpserver.main.CommandLine

capstan runを呼んでディスクイメージを生成・実行します。

FTPサーバに接続可能にするため、ネットワークブリッジ接続にします。

jar -cvfm ../ftpserver.jar boot-manifest.mf .
cd ..
~/go/bin/capstan run -n bridge ftpserver.jar

capstan runが実行されると以下のような感じでFtpServerが起動します。

Building ftpserver...
Uploading files...
1 / 1 [=============================================================] 100.00 % 0Created instance: ftpserver
OSv v0.16
eth0: 192.168.122.77
Using default configuration
log4j:WARN No appenders could be found for logger (org.apache.ftpserver.impl.DefaultFtpServerContext).
log4j:WARN Please initialize the log4j system properly.
FtpServer started

ftpクライアントで繋いでみます。

$ ftp 192.168.122.77
Connected to 192.168.122.77 (192.168.122.77).
220 Service ready for new user.
Name (192.168.122.77:syuu): anonymous
331 Guest login okay, send your complete e-mail address as password.
Password:
530 Authentication failed.
Login failed.
ftp>

デフォルト設定の場合有効ユーザ名・パスワードが分からなかったのでログイン失敗しましたが、FTPサーバが上がっているようです。

2014-12-10

[][]OSv CLIコマンドを追加しよう

OSv CLIの使い方 - かーねる・う゛いえむにっきで見たとおり、現状CLIコマンドは非常に少なく、REST APIを直接呼び出して一部の操作を行わざるを得ない状態です。

そこで、コマンドを足してOSvへコントリビューションしてみるのは如何でしょうか。

例えば、dateコマンドは以下のような非常に簡単なluaスクリプトで出来ています:

local cmd = {}

cmd.desc = [[print the system date and time]]
cmd.help = [[Usage: date

Print OSv reported date.]]

cmd.main = function()
  local content, status = osv_request({"os", "date"}, "GET")
  osv_resp_assert(status, 200)
  io.write(content, '\n')
end

return cmd

osv_request()によってREST APIが呼び出され、結果がcontentに代入されてio.write()でコンソールに出力されています。

コマンド定義するluaスクリプトはmodules/cli/commands/に コマンド名.lua という名前で配置することになっています。

まだまだapiコマンド以外からアクセス出来ないREST API上の機能がいくつもあるので、ぜひ新しいコマンドを作成してみて下さい。

[]OSv CLIの使い方

リモートCLIについて - かーねる・う゛いえむにっきでも紹介したCLIの使い方についてです。

リモートCLI必要なもの

まず、Linuxホスト側でCLIインストールする必要があります(参照)。

その上で、httpserver(REST APIサーバ)を含むOSvのイメージを実行する必要があります。

Capstan経由で配布されているディスクイメージやosv.ioで配布されているディスクイメージは全てhttpserverを含んだイメージですが、OSvをソースコードからビルドする場合はhttpserverを明示的に指定する必要があります。

つまり、

git clone https://github.com/cloudius-systems/osv.git
cd osv
git submodule update --init --recursive
make image=ruby-publify

ではなく、

git clone https://github.com/cloudius-systems/osv.git
cd osv
git submodule update --init --recursive
make image=ruby-publify,httpserver

のようにビルドする必要があります。

CLI on OSvに必要なもの

CLIアプリケーションとで1つのコンソールを共有するので、アプリケーションと同時にCLIを実行出来るのはアプリケーション継続的コンソール出力を行わない・コンソール入力要求していない場合に限ります(OSvに切り替え可能な仮想コンソールはありません)。

コンソールの使用が競合してもCLIアプリケーションは正常に動作しますが、出力や入力が入り交じって使えないものになってしまいます。

例えばTomcatCLIを実行する場合は、

git clone https://github.com/cloudius-systems/osv.git
cd osv
git submodule update --init --recursive
make image=tomcat,cli

のようにビルドするとTomcatCLIが同時に起動されるイメージを作る事が出来ます。

Capstanで配布されているイメージとしてcloudius/osvというCLIのみを含んだものがありますが、このイメージではJavaや他の言語ランタイムを一切含んでいないため、簡単にユーザプログラムを追加・実行するのが難しくあまり役に立ちません(※ネイティブバイナリを試すことは簡単にできます)。

CLIの使い方

help

help でコマンド一覧を表示します。

help <コマンド名>でコマンド毎の使い方を表示します。

exit

OSを停止します。poweroffと同じ動作になります。

Unixコマンド

cat, cd, date, df, dmesg, echo, free, ls, mkdir, poweroff, reboot, rm, top, uptimeについては同名のUnixコマンド風の動作を行います(引数などがPOSIX準拠になっているわけではありません)。

api

他のコマンド群はREST APIをラップして通常のUnixコマンドなどのように振る舞うのに対し、apiコマンドユーザが直接REST API操作するためのものです。

使い勝手は他のコマンド群に劣りますが、現状ではapiコマンドを経由してしかアクセス出来ない機能が多数存在するため、このコマンドを使う事がほとんど必須になります。

apios, jvm, networkなどのAPIクラス名の一覧が表示されます。

api <クラス名> でAPIクラスが持つメソッドが表示されます。この時、HTTPメソッド種別特に書いていない説明の行はGETメソッドで、[POST]と書かれている行はPOSTメソッドになります。

GET以外のHTTPメソッドを実行するには、api -m <HTTPメソッド名>のように引数を渡す必要があります。

例えばos.rebootを実行する場合は以下のようになります:

# api -m POST os reboot

POSTにパラメータ必要場合は以下のように「変数名=値」の形式で指定します。

/# api os hostname
osv.local
/# api -m POST os hostname name=osv1

/# api os hostname
osv1

POSTに必要な変数名などの細かい情報CLIヘルプには表示されないので、WebブラウザRESTサーバへ接続してREST APIドキュメントを読んで下さい。

REST APIドキュメントは http://<OSvのIP>:8000/ へアクセスして、左側のRESTのタブを選ぶ事で読むことが出来ます。

常に最新版がアップロードされているとは限りませんが、REST APIドキュメントはこちらにミラーされています:OSv API

2014-12-09

[]Capstanの使い方

CapstanはGo言語実装されたOSvのデプロイツールです。

各種アプリケーションを含んだディスクイメージを1コマンドダウンロードし、ローカルKVMVirtualBoxなどへデプロイすることが出来ます。

以下に使い方を紹介します。

Capstanの対応環境

対応OSLinux, Mac, Windowsの3つです。

対応しているハイパーバイザIaaSQEMU(可能ならKVM)、VirtualBoxVMwareGoogle Compute Engineです。

ハイパーバイザを指定しない場合デフォルトは、LinuxQEMU(可能ならKVM)、Mac, WindowsVirtualBoxです。

インストール方法

Linux / Mac
curl https://raw.githubusercontent.com/cloudius-systems/capstan/master/scripts/download | bash
Windows

ダウンロード

コマンドヘルプ(capstan help)

helpでコマンドの一覧を、help <コマンド名>でコマンドの詳細と引数の説明を読むことが出来ます。

capstan help
capstan help run

リモートイメージ検索(capstan search)

search <アプリ名>でアプリ名を含むリモートイメージ検索を行います。

searchだけで実行すると全てのリモートイメージの一覧を表示します。

capstan search tomcat
capstan search

リモートイメージダウンロード(capstan pull)

pull <リモートイメージ名>でイメージダウンロードします。

capstan pull cloudius/osv-tomcat

ダウンロード済みリモートイメージの一覧(capstan images)

ダウンロード済みのイメージ一覧を表示します。

ダウンロード済みのイメージはrunコマンドにより再ダウンロード必要無く実行出来ます。

capstan images

リモートイメージ・停止済みインスタンスの実行(capstan run)

run <リモートイメージ名>でリモートイメージダウンロード&実行します。既にダウンロードされている場合ダウンロード済みイメージを使用します。

run <インスタンス名>で停止済みインスタンスを実行します。

引数無しのrunではカレントディレクトリのCapstanfileからbuildコマンドで生成されたインスタンスを実行します。

以下にオプション引数の説明をします。

capstan run cloudius/osv
capstan run -m 4G -c 4 -f '8000:8000' -f '8081:8081' cloudius/osv-tomcat

インスタンスの一覧(capstan instances)

instancesでインスタンスの一覧を表示します。

capstan instances

実行中インスタンスの停止(capstan stop)

stop <インスタンス名>で実行中インスタンスを停止します。

capstan stop cloudius-osv-tomcat

インスタンスの削除(capstan delete

delete <インスタンス名>でインスタンスを削除します。

capstan delete cloudius-osv-tomcat

[][]OSvでLuaインタープリタを動かす

OSvは、ハイパーバイザやIaaSプラットフォームアプリケーションデプロイすることに特化した軽量OSです(※詳しくはこちら)。

OSvでは簡易CLI実装するためにLua言語が使用されています。

この記事では、CLI以外の任意LuaプログラムをOSv上で実行してみます。

OSvのビルド

OSvのgitリポジトリからソースコードをcloneします。

git clone https://github.com/cloudius-systems/osv.git
cd osv
git submodule update --init --recursive

OSvに含まれるLuaビルドスクリプトはliblua.soだけをOSvインスタンスインストールするようになっており、luaコマンドインストールされません。

取り敢えずluaコマンドを実行してインタラクティブLuaプログラムを実行してみたいので、以下のようなパッチを当てます。

diff --git a/modules/lua/Makefile b/modules/lua/Makefile
index 64ea417..88ae0e6 100644
--- a/modules/lua/Makefile
+++ b/modules/lua/Makefile
@@ -33,12 +33,13 @@ MAIN=src/liblua.so

 # Local executable used for LuaRocks
 LUA_BIN=$(BDIR)/lua
+LUA_BIN_SO=$(BDIR)/lua.so
 LUA_ROCKS_BIN=$(BDIR)/luarocks

 # List of Lua modules, each module has its own target
 LUA_MODULES=LuaSocket LuaJSON Lua_stdlib LuaFileSystem LuaPath LuaSec

-module: $(MAIN) $(LUA_MODULES)
+module: $(MAIN) $(LUA_MODULES) $(LUA_BIN_SO)

 $(MAIN): $(SRCS)
 	cd src && $(MAKE)
@@ -46,6 +47,10 @@ $(MAIN): $(SRCS)
 $(LUA_BIN): $(MAIN) | $(BDIR)
 	cp src/lua $(LUA_BIN)

+$(LUA_BIN_SO):
+	cd src && gcc -o lua.so lua.o liblua.a -lm -ldl -shared
+	cp src/lua.so $(LUA_BIN_SO)
+
 $(LUA_ARCHIVE): | $(DOWNLOAD)
 	cd $(DOWNLOAD) && \
 	 curl --remote-name --remote-time $(LUA_DOWNLOAD)
diff --git a/modules/lua/module.py b/modules/lua/module.py
index b15ebbc..0d3b6b0 100644
--- a/modules/lua/module.py
+++ b/modules/lua/module.py
@@ -8,3 +8,5 @@ usr_files.add('${OSV_BASE}/modules/lua/out').to('/usr') \
 	.exclude('etc/*') \
 	.exclude('lib/luarocks/**') \
 	.exclude('share/lua/*/luarocks/**')
+
+usr_files.add('${OSV_BASE}/modules/lua/out/bin/lua.so').to('/lua.so')

OSvをLua入りでビルドします。

make image=lua

OSv上でLuaを実行

scripts/run.pyでOSvインスタンスKVM上で起動するとLuaインタプリタが起動してインタラクティブLuaスクリプトが実行出来ます。

./scripts/run.py -n -e /lua.so
OSv v0.16-14-g2514672
eth0: 192.168.122.89
Lua 5.2.3  Copyright (C) 1994-2013 Lua.org, PUC-Rio
>
>
> print("Hello world")
Hello world
> print(type("Hello world"))
string
> = 1 + 2
3

2014-12-03

[]リモートCLIについて

OSvには簡易シェルとしてLua CLIが用意されていて、capstan経由でLua CLIしか入ってないVMイメージが配布されていたりするのですが、実はこのCLIは全てREST API上に実装されています。

つまり、常にhttp://127.0.0.1:8000/RESTを送ってシステムAPIを叩いており、そのままこのCLI実装をOSvの外側に持って行けばリモートCLIとして使う事が出来ます。

そもそもそういう意図実装されていたのですが、最近までOSvの外側で動かすためのテストと配布イメージ作成が行われていませんでした。

最近になってLinux環境インストール可能な配布イメージを作成出来るようになったので、ビルド方法インストール方法・使い方をご紹介します。

リモートCLIビルド方法

ビルド済みイメージを後述のURLから配布しますので、通常この作業は必要ありません。

ディストリビューションでパッケージを作る用途などにお使い下さい。

なお、このビルド方法は今の所Fedora20でのみ動作が確認されています。

git clone https://github.com/cloudius-systems/osv.git
cd osv
git submodule update --init --recursive
sudo scripts/setup.py
cd modules/cli
make rpm

以上のコマンドでrpmbuild/RPMS/x86_64/osv-cli-1.0-1.fc20.x86_64.rpmRPM形式で、rpmbuild/SOURCES/osv-cli-1.0.tar.gzにtar.gz形式で配布イメージが出力されます。

ビルド済みイメージダウンロード

osv-cli-1.0-1.fc20.x86_64.rpm

osv-cli-1.0.tar.gz

配布イメージインストール

RPM場合
yum install osv-cli-1.0-1.fc20.x86_64.rpm
tar.gzの場合
tar xpf osv-cli-1.0.tar.gz
mv osv-cli-1.0/usr/lib64/osv-cli /usr/lib64
echo "(cd /usr/lib64/osv-cli && exec ./cli \$@)" > /usr/bin/cli
chmod a+rx /usr/bin/cli

使い方

シェル1(OSvを起動)
$ capstan run -n 'bridge' cloudius/osv-tomcat
Created instance: cloudius-osv-tomcat
OSv v0.16
eth0: 192.168.122.72

IPアドレスメモ

シェル2(リモートCLIを起動)
$ cli -a http://192.168.122.72:8000
/# ls
dev/             etc/             init/            java/
java.so          libhttpserver.so libtools.so      libuutil.so
libzfs.so        proc/            tmp/             tools/
usr/             zfs.so           zpool.so
/# cat etc/hosts
127.0.0.1 localhost osv.local

/# api os version
v0.16
/#

次回予告

今回はRESTサーバに対するセキュリティ設定(鍵認証設定)、CLIの使い方に言及しなかったので、次回はこのあたりを解説していきます。

追伸

Lua実装されたRESTベースシェルってやつのソースコードが読んでみたいという方、このへんです。