2018年12月12日水曜日

VDDI API の VixDiskLib_QueryAllocatedBlocks を使ってみた

概要

VixDiskLib_QueryAllocatedBlocks は VDDK API 6.7 から追加された API です
指定の vmdk ですでに使われているブロック領域を取得することができます

環境

  • CentOS 7
  • VCSA 6.5.0 9451637
  • VDDK API 6.7.1

サンプルコード

コード全体は以下の通りです
ポイントは後半になります
このあとで説明します

#include <iostream>
#include <cstring>
#include "vixDiskLib.h"
using std::cout;
using std::endl;

#define VIXDISKLIB_VERSION_MAJOR 6
#define VIXDISKLIB_VERSION_MINOR 7

static struct {
  VixDiskLibConnection connection;
  char *libdir;
  char *cfgFile;
} params;

static void LogFunc(const char *fmt, va_list args) {
   printf("Log: ");
   vprintf(fmt, args);
}

static void WarnFunc(const char *fmt, va_list args) {
   printf("Warning: ");
   vprintf(fmt, args);
}

static void PanicFunc(const char *fmt, va_list args) {
   printf("Panic: ");
   vprintf(fmt, args);
   exit(10);
}

int main() {
  VixError err;

  err = VixDiskLib_InitEx(VIXDISKLIB_VERSION_MAJOR, VIXDISKLIB_VERSION_MINOR, &LogFunc, &WarnFunc, &PanicFunc, params.libdir, "init.cfg");
  printf("%lu\n", err);

  VixDiskLibConnectParams cnxParams = {0};
  cnxParams.vmxSpec = {(char*)"moref=vm-20"};
  cnxParams.specType = VIXDISKLIB_SPEC_VMX;
  cnxParams.serverName = {(char*)"192.168.100.20"};
  cnxParams.credType = VIXDISKLIB_CRED_UID;
  cnxParams.creds.uid.userName = {(char*)"administrator@vsphere.local"};
  cnxParams.creds.uid.password = {(char*)"xxxxxxxxxxxx"};
  cnxParams.thumbPrint = {(char*)"96:09:d6:5b:e0:83:58:1b:ba:2b:cc:78:22:88:33:36:64:50:32:eb"};
  err = VixDiskLib_ConnectEx(&cnxParams, 0, NULL, "nbd", &params.connection);
  printf("%lu\n", err);

  VixDiskLibHandle _handle = NULL;
  err = VixDiskLib_Open(params.connection, "[datastore2] dist/dist.vmdk", VIXDISKLIB_FLAG_OPEN_SINGLE_LINK , &_handle);
  printf("%lu\n", err);

  VixDiskLibBlockList *buf = new VixDiskLibBlockList;
  VixDiskLibSectorType start = 0;
  VixDiskLibSectorType sectorCount = 62914560; // -> 30GB / 512
  VixDiskLibSectorType chunk = 8192;
  // VixDiskLibSectorType chunk = 128;
  VixDiskLibSectorType sum = 0;
  err = VixDiskLib_QueryAllocatedBlocks(_handle, start, sectorCount, chunk, &buf);
  printf("%lu\n", err);
  printf("%lu\n", buf->numBlocks);
  for (int i = 0; i < buf->numBlocks; i++) {
    VixDiskLibBlock vb = buf->blocks[i];
    printf("i: %d\n", i);
    printf("offset: %lu\n", vb.offset);
    printf("length: %lu\n", vb.length);
    sum = sum + vb.length;
  }
  printf("sum length: %lu\n", sum / chunk);

  delete[] buf;

  VixDiskLib_Disconnect(params.connection);
  return 0;
}

説明

VixDiskLib_QueryAllocatedBlocks には 5 つの引数が必要になります
_handle はディスクのハンドラです

start が探索を開始するオフセットです
基本は 0 を指定すれば OK です

sectorCount は操作対象のディスクのセクタ数を入力します
今回であれば 30GB のシンプロビジョニングのディスクを対象にしています
シンプロビジョニングなのでディスク使用量はもっと少ないかもしれませんがここで指定する値は VM に接続したハードディスクのサイズを入力してください
なおこの値は mob で取得できます
config.hardware.device[2000]capacityInBytes を 512 で割った値になります
セクタの求め方は「ディスクサイズ / 512」になります
512 は VIXDISKLIB_SECTOR_SIZE の値になります
1 セクタに含まれるバイト数で割ることでセクタ数を算出することができます

chunk は取得する領域のサイズの単位 (?) になります
VixDiskLib_QueryAllocatedBlocks は結果としてブロック数が取得できるのですが、その数が chunk の大きさによって変わります
chunk を大きくすればブロック数が少なくなり、chunk を小さくすればブロック数が多くなります
また chunk は 128 から 131072 の範囲で指定する必要があります

最後の buf は結果を格納する VixDiskLibBlockList のポインタ変数です
関数に渡す場合は更にポイントとして渡します

VixDiskLibBlockList *buf = new VixDiskLibBlockList;
VixDiskLibSectorType start = 0;
VixDiskLibSectorType sectorCount = 62914560; // -> 30GB / 512
VixDiskLibSectorType chunk = 8192;
// VixDiskLibSectorType chunk = 128;
VixDiskLibSectorType sum = 0;
err = VixDiskLib_QueryAllocatedBlocks(_handle, start, sectorCount, chunk, &buf);

あとは結果を参照します

for (int i = 0; i < buf->numBlocks; i++) {
  VixDiskLibBlock vb = buf->blocks[i];
  printf("i: %d\n", i);
  printf("offset: %lu\n", vb.offset);
  printf("length: %lu\n", vb.length);
  sum = sum + vb.length;
}

buf->numBlocks にブロック数が格納されているのでその分ループします
ブロック情報は buf->blocks に配列として格納されています
VixDiskLibBlock の構造体として取得できるのであとはそれに含まれる offsetlength を表示するだけです

よくわからなかったこと

chunk サイズを変更させると length の合計が変わりました
結果として取得できるブロックが指定の chunk サイズ単位ごとに取得できるため実際には使われていない領域も結果に含まれている可能性がありそうです
128 を指定したほうが sum が小さくなったのでおそらくそういうことなのではと推測しています

またこの VixDiskLib_QueryAllocatedBlocks は vSphere API の QueryChangedDiskAreas と組み合わせて使うようです
VixDiskLib_QueryAllocatedBlocks でアロケート済みの領域を取得してかつ QueryChangedDiskAreas で変更のあった領域を取得し双方の領域の積集合を取ることで増分領域を正確に取得できることができるようになるようです (参考: バックアップとリカバリの例)

QueryChangedDiskAreas のレスポンスも offset と length のリストなので確かに言っていることはできそうなのですが実は QueryChangedDiskAreas 側の引数に chunk サイズを指定するオプションがありません
今回の検証結果を見ると VixDiskLib_QueryAllocatedBlocks のである offset と length の値は chunk サイズによって変わるので QueryChangedDiskAreas 側の chunk サイズがわからないことには比較できないかなと思っています

もしかすると情報が出てくるのかもしれませんが自分が調べた限りでは出てきませんでした
やろうとしたら QueryChangedDiskAreas の「*」で取得した結果 (offset, length) と VixDiskLib_QueryAllocatedBlocks で取得した結果 (offset, length) を chunk サイズを変更しつつ比較して QueryChangedDiskAreas の chunk サイズを見つけるしかないのかもしれません
(それか実は自分の理解が全く違っていて、そもそも双方の API は比較することができないのかもしれません、、)

P.S

あとでわかったのですが QueryChangedDiskAreasVixDiskLib_QueryAllocatedBlocks はそもそも結果を比較して使うものではなさそうです
VixDiskLib_QueryAllocatedBlocks はフルバックアップ用で QueryChangedDiskAreas が増分バックアップ用になっているようです

それ以外で気になったこと

chunk サイズ以外で気になったこととしては length の単位です
調べるとバイトらしいのですが合計したバイトサイズと実際の vmdk のサイズが全然違っていました
実際の vmdk のサイズと比較する場合は length だけではダメなのかもしれません

最後に

VixDiskLib_QueryAllocatedBlocks をコールしてみました
使い方はわかったのですが結果が何を示しているかいまいち理解することができませんでした
VDDK API 内で扱われるセクタ、チャンクサイズ、ブロック、offset、length が何を意味しているか理解しないとそもそも VDDK API を使いこなすのは難しいのかもしれません

1 件のコメント:

  1. 无意中在网上搜到你的文档,我正在做vmware的全量、增量备份。虽然已经有很多付费软件实现了此功能。但是目前还没有看到一些开源的软件。目前全量备份的功能我已经通过VixDiskLib_QueryAllocatedBlocks实现了,但是增量功能还有些问题,多次运行增量备份后磁盘数据会损坏😒。this chinese,you can translate to japanese

    返信削除