ブロックストレージサービスで車輪の再開発(3)

外部コマンド叩く話のつづき2

さて、今日は _create_qcow2_file() の部分です。

https://github.com/openstack/cinder/blob/master/cinder/volume/drivers/nfs.py#L240-L246

qemu-img コマンドを叩かない方法はどうしたらいいんだろう?と思い、とりあえず qemu-img でイメージを生成してみました。すると以下のように一部のヘッダ部分だけ値の入っている 193KB 程度のファイルが生成されます。

$ xxd ~/test.img |grep -v ": 0000 0000 0000 0000 0000 0000 0000 0000  ................"
0000000: 5146 49fb 0000 0003 0000 0000 0000 0000  QFI.............
0000010: 0000 0000 0000 0010 0000 0000 4000 0000  ............@...
0000020: 0000 0000 0000 0002 0000 0000 0003 0000  ................
0000030: 0000 0000 0001 0000 0000 0001 0000 0000  ................
0000060: 0000 0004 0000 0068 0000 0000 0000 0000  .......h........
0010000: 0000 0000 0002 0000 0000 0000 0000 0000  ................
0020000: 0001 0001 0001 0001 0000 0000 0000 0000  ................

で、このヘッダ部分を生成しているのが、Qemu の block/qcow2.c の qcow2_create2() の以下の部分(かな?)と思い、

    /* Write the header */
    QEMU_BUILD_BUG_ON((1 << MIN_CLUSTER_BITS) < sizeof(*header));
    header = g_malloc0(cluster_size);
    *header = (QCowHeader) {
        .magic                      = cpu_to_be32(QCOW_MAGIC),
        .version                    = cpu_to_be32(version),
        .cluster_bits               = cpu_to_be32(cluster_bits),
        .size                       = cpu_to_be64(0),
        .l1_table_offset            = cpu_to_be64(0),
        .l1_size                    = cpu_to_be32(0),
        .refcount_table_offset      = cpu_to_be64(cluster_size),
        .refcount_table_clusters    = cpu_to_be32(1),
        .refcount_order             = cpu_to_be32(3 + REFCOUNT_SHIFT),
        .header_length              = cpu_to_be32(sizeof(*header)),
    };

    if (flags & BLOCK_FLAG_ENCRYPT) {
        header->crypt_method = cpu_to_be32(QCOW_CRYPT_AES);
    } else {
        header->crypt_method = cpu_to_be32(QCOW_CRYPT_NONE);
    }

    if (flags & BLOCK_FLAG_LAZY_REFCOUNTS) {
        header->compatible_features |=
            cpu_to_be64(QCOW2_COMPAT_LAZY_REFCOUNTS);
    }

「じゃぁ、これを Python で書けば」……(ヾノ・∀・`)ナイナイ

書きません。そこまでして「コマンド叩くの好きじゃない」とか言い張るほど強い主張はないです。 っていうことで、コマンド叩く実装のほうがトータルコストが低いなら、コマンドでもいいんじゃないですかね?と思い直しました。1

run_as_root=Trueの謎

1回目のときに「何で run_as_root=True で叩いているんだろう?」って書いちゃったので、全く觝れないわけにはいかないと思い、ちょっぴり觝れます。

とりあえず、Cinder のボリュームが格納されているところを見てみました。

$ LANG=C sudo ls -al /var/lib/cinder/volumes/
total 12
drwxr-xr-x 2 cinder cinder 4096 May 29  2013 .
drwx------ 3 cinder cinder 4096 May 29  2013 ..
-rw-r--r-- 1 cinder cinder  228 Mar 11 22:57 volume-5b519288-53a4-48fc-8ccf-eb5accea7001

うーん、ディレクトリの権限やはり Cinder ユーザの権限で作成できますよね…。わざわざ root で叩く必要性がわからないですが、もっとちゃんと見ていけば理由がわかるかもしれませんが、そこまで OpenStack のコードを追いたくないので諦めます。

どんな機能にするかの話

やっと本題です。もうサクっと作ってしまおうかと思い、とりあえず機能をザックリと決めたいと思います。

まず最初に最低限実装しておきたい機能は以下です。なお、今後実装する段階で、削ったり盛ったりします。(行き当たりばったりなのでσ^^:)

  • ボリュームの一覧 – 現在作成されているボリュームの一覧を表示する機能
    • インプット: ボリューム ID(任意)
    • アウトプット: ボリューム ID、ボリュームサイズ、ボリューム作成日、NFS の共有パス
  • ボリュームの作成 – ボリュームを作成する機能
    • インプット: ボリュームのサイズ(必須)、ボリュームの名称(必須)、NFS のオプション(任意)
    • アウトプット: ボリューム作成の成否、ボリューム ID、ボリュームのサイズ、ボリュームの作成日、NFSの共有パス
  • ボリュームの削除
    • インプット: ボリューム ID(必須)
    • アウトプット: ボリュームの削除の成否

なお、上記の3機能のそれぞれの処理概要は以下になります。

  • ボリュームの一覧

    1. インプットでボリューム ID が指定されていれば該当するボリュームを表示し、指定されていなければ全てのボリュームを表示
  • ボリュームの作成

    1. インプットに指定されたボリュームの名称が存在すれば既に同じ名前のボリュームが存在する旨を返答し終了
    2. インプットに指定されたボリュームのサイズでスパースファイルを作成
    3. 作成したボリュームファイルを何らかのファイルシステムでフォーマット(ファイルシステムは後で検討する)
    4. ボリュームファイルをマウント
    5. /etc/exports を変更し、exportfs を叩いて変更を反映させる
    6. NFSの共有パスなどのボリュームに対する情報をアウトプット
  • ボリュームの削除

    1. インプットに指定されたボリュームの ID が無ければエラーをアウトプット
    2. 当該ボリュームに該当する設定を /etc/exports から削除し、exportfs を叩いて変更を反映させる
    3. ボリュームファイルをアンマウント
    4. ボリュームファイルを削除

こんなザックリした感じで進めていこうと思います。今日はこれまで。明日から少しづつ実装していくお話。ちなみに言語はまだ決めてません。



  1. ただし、lvm コマンドをバシバシ叩きまくるのはイケてないです。そんなに lvm は叩かれることを想定してないと思うので…’ [return]