VMware 虚拟化编程(7) — VixDiskLib 虚拟磁盘库详解之三

时间:2022-01-24 15:10:02

目录

前文列表

VMware 虚拟化编程(1) — VMDK/VDDK/VixDiskLib/VADP 概念简析

VMware 虚拟化编程(2) — 虚拟磁盘文件类型详解

VMware 虚拟化编程(3) —VMware vSphere Web Service API 解析

VMware 虚拟化编程(4) — VDDK 安装

VMware 虚拟化编程(5) — VixDiskLib 虚拟磁盘库详解之一

VMware 虚拟化编程(6) — VixDiskLib 虚拟磁盘库详解之二

VixDiskLib 虚拟磁盘库

紧接上篇。

VixDiskLib_GetMetadataKeys & VixDiskLib_ReadMetadata 获取虚拟磁盘元数据

函数原型

/**
* Retrieves the list of keys in the metadata table.
* Key names are returned as list of null-terminated strings,
* followed by an additional NULL character.
* @param diskHandle [in] Handle to an open virtual disk.
* @param keys [out, optional] Keynames buffer, can be NULL.
* @param maxLen [in] Size of the keynames buffer.
* @param requiredLen [out, optional] Space required for the keys including the double
* end-of-string characters.
* @return VIX_OK if success, suitable VIX error code otherwise.
*/
VixError
VixDiskLib_GetMetadataKeys(VixDiskLibHandle diskHandle,
char *keys,
size_t maxLen,
size_t *requiredLen); /**
* Retrieves the value of a metadata entry corresponding to the supplied key.
* @param diskHandle [in] Handle to an open virtual disk.
* @param key [in] Key name.
* @param buf [out, optional] Placeholder for key's value in the metadata store,
* can be NULL.
* @param bufLen [in] Size of the buffer.
* @param requiredLen [out, optional] Size of buffer required for the value (including
* end of string character)
* @return VIX_OK if success, VIX_E_DISK_BUFFER_TOO_SMALL if too small a buffer
* and other errors as applicable.
*/
VixError
VixDiskLib_ReadMetadata(VixDiskLibHandle diskHandle,
const char *key,
char *buf,
size_t bufLen,
size_t *requiredLen);

函数调用

vixError = VixDiskLib_ReadMetadataKeys(diskHandle, &buf[0], requiredLen, NULL);
vixError = VixDiskLib_ReadMetadata(diskHandle, metakey, &val[0],requiredLen, NULL);

元数据表中包含了磁盘卷标、LUN、分区布局、链接个数、文件属性、锁、RDM 封装等信息,Hosted Disk 和 Managed Disk 的元数据也并不相同。

  • VixDiskLibAdapterTypea dapterType
  • VixDiskLibSectorType capacity
  • int numLinks
  • char * parentFileNameHint
  • VixDiskLibGeometry biosGeo
  • VixDiskLibGeometry physGeo
    • typedef uint32 heads
    • typedef uint32 cylinders
    • typedef uint32 sectors
adapterType= buslogic
geometry.heads= 64
geometry.cylinders= 100
geometry.sectors= 32
uuid= 60 00 c2 93 7b a0 3a 03 9f 22 56 c5 29 93 b7 27

需要注意的是,VixDiskLib_ReadMetadataKeys 获取的虚拟磁盘的元数据表中的 key,而非 value,其需要和 VixDiskLib_ReadMetadata 结合使用。

VixDiskLib_WriteMetadata 更新虚拟磁盘元数据表

函数原型

/**
* Creates or modifies a metadata table entry.
* @param diskHandle [in] Handle to an open virtual disk.
* @param key [in] Key name.
* @param val [in] Key's value.
* @return VIX_OK if success, suitable VIX error code otherwise.
*/
VixError
VixDiskLib_WriteMetadata(VixDiskLibHandle diskHandle,
const char *key,
const char *val);

函数调用

vixError = VixDiskLib_WriteMetadata(diskHandle, metakey, metaVal);

VixDiskLib_WriteMetadata 通过指定 Key/Value 对来更新虚拟磁盘的元数据。如果 Key 不存在,则会新添一项;如果 Key 存在,则更新 Value。Key 可以被设置为空,但是不能删除。

VixDiskLib_Create 创建新的寄宿磁盘 Hosted Disk

函数原型

/**
* Creates a local disk. Remote disk creation is not supported.
* @param connection [in] A valid connection.
* @param path [in] VMDK file name given as absolute path
* e.g. "c:\\My Virtual Machines\\MailServer\SystemDisk.vmdk".
* @param createParams [in] Specification for the new disk (type, capacity ...).
* @param progressFunc [in] Callback to report progress.
* @param progressCallbackData [in] Callback data pointer.
* @return VIX_OK if success suitable VIX error code otherwise.
*/
VixError
VixDiskLib_Create(const VixDiskLibConnection connection,
const char *path,
const VixDiskLibCreateParams *createParams,
VixDiskLibProgressFunc progressFunc,
void *progressCallbackData);

函数调用

vixError = VixDiskLib_Create(connectionHandle, diskPath, &createParams, NULL, NULL);

当你使用 VixDiskLib_ConnectEx 或 VixDiskLib_Connect 连接到 ESX/ESXi Host 之后,就可以调用该函数创建一个新的 hosted disk(本地磁盘)。

  • @param createParams:需要指定 磁盘类型、适配器类型、硬件版本,容量 等信息。
    • typedef VixDiskLibDiskType diskType
    • typedef VixDiskLibAdapterType adapterType
    • typedef uint hwVersion
    • typedef VixDiskLibSectorType capacity

NOTE:在 FAT32 以及 FAT 文件系统上,VixDiskLib_Create 要求 hosted disk 的 size 小于 4GB;NTFS 文件系统上,hosted disk 的 size 小于 16TB-54KB(hex FFFFFFF0000);在 ReFS 和 exFAT 文件系统,hosted disk 的 size 小于 2^64 ‐ 1。在最新的 vSphere5.5 以及更新版本中,将支持大于 2TB 的 VMDK File。包括 NFS3 在内的基于 POSIX 的文件系统不再有 2GB 的 VMDK File Size Limit。

VixDiskLib_Clone 克隆 VMDK File

函数原型

/**
* Copies a disk with proper conversion.
* @param dstConnection [in] A valid connection to access the destination disk.
* @param dstPath [in] Absolute path for the (new) destination disk.
* @param srcConnection [in] A valid connection to access the source disk.
* @param srcPath [in] Absolute path for the source disk.
* @param vixCreateParams [in] creationParameters (disktype, hardware type...).
* If the destination is remote, createParams is currently
* ignored and disk with default size and adapter type is
* created.
* @param progressFunc [in] Callback to report progress (called on the same thread).
* @param progressCallbackData [in] Opaque pointer passed along with the percent
* complete.
* @param overWrite [in] TRUE if Clone should overwrite an existing file.
* @return VIX_OK if success, suitable VIX error code otherwise (network errors like
* file already exists
* handshake failure, ...
* are all combined into a generic connect message).
*/
VixError
VixDiskLib_Clone(const VixDiskLibConnection dstConnection,
const char *dstPath,
const VixDiskLibConnection srcConnection,
const char *srcPath,
const VixDiskLibCreateParams *vixCreateParams,
VixDiskLibProgressFunc progressFunc,
void *progressCallbackData,
Bool overWrite);

函数调用

vixError = VixDiskLib_Clone(dstconnection, dstPath, srcConnection, srcPath, &createParams, CloneProgressFunc, NULL, TRUE);

VixDiskLib_Clone 函数能够将一个 VMDK File 中的数据拷贝到另一个 VMDK File 中,支持指定 磁盘类型,磁盘大小,硬件版本 等信息。

创建新的托管磁盘 Managed Disk

因为 VixDiskLib_Create 函数仅支持创建本地的 hosted disk,所以如果你希望创建一个 Remote Managed Disk 的话,就需要使用特别的方式。

  • Step 1:首先调用 VixDiskLib_Create 创建一个 hosted disk
  • Step 2:然后调用 VixDiskLib_Clone 将 hosted disk 的数据克隆到 managed disk。

VixDiskLib_Unlink 删除 VMDK File

函数原型

/**
* Deletes all extents of the specified disk link. If the path refers to a
* parent disk, the child (redo log) will be orphaned.
* Unlinking the child does not affect the parent.
* @param connection [in] A valid connection.
* @param path [in] Path to the disk to be deleted.
* @return VIX_OK if success, suitable VIX error code otherwise.
*/ VixError
VixDiskLib_Unlink(VixDiskLibConnection connection,
const char *path);

函数调用

vixError= VixDiskLib(connectionHandle, diskpath);

该函数需要提供两个参数,连接句柄和 VMDK File Name,调用该函数能够彻底删除 VMDK File。需要注意的是,删除一个 VMDK 后,它包含的所有信息都将丢失。一般的,如果虚拟机正在运行,那么 HostOS 将会阻止你删除 VMDK File。 但如果你删除了一个电源已关闭的虚拟机的 VMDK 文件,那么这个 GuestOS 将无法启动。

VixDiskLib_Grow 扩展 VMDK File

函数原型

/**
* Grows an existing disk, only local disks are grown.
* @pre The specified disk is not open.
* @param connection [in] A valid connection.
* @param path [in] Path to the disk to be grown.
* @param capacity [in] Target size for the disk.
* @param updateGeometry [in] Should vixDiskLib update the geometry?
* @param progressFunc [in] Callback to report progress (called on the same thread).
* @param progressCallbackData [in] Opaque pointer passed along with the percent
* complete.
* @return VIX_OK if success, suitable VIX error code otherwise.
*/
VixError
VixDiskLib_Grow(VixDiskLibConnection connection,
const char *path,
VixDiskLibSectorType capacity,
Bool updateGeometry,
VixDiskLibProgressFunc progressFunc,
void *progressCallbackData);

函数调用

vixError= VixDiskLib_Grow(connectionHandle, diskpath, size, FALSE, GrowProgressFunc, NULL);

调用 VixDiskLib_Grow 函数能够通过增加扇区数量的方式来扩展一个已存在的 VMDK File,但该函数同样仅支持 hosted disk。

VixDiskLib_Shrink 压缩 VMDK File

函数原型

/**
* Shrinks an existing disk, only local disks are shrunk.
* @param diskHandle [in] Handle to an open virtual disk.
* @param progressFunc [in] Callback to report progress (called on the same thread).
* @param progressCallbackData [in] Opaque pointer passed along with the percent
* complete.
* @return VIX_OK if success, suitable VIX error code otherwise.
*/
VixError
VixDiskLib_Shrink(VixDiskLibHandle diskHandle,
VixDiskLibProgressFunc progressFunc,
void *progressCallbackData);

函数调用

vixError= VixDiskLib_Shrink(diskHandle, ShrinkProgressFunc, NULL);

调用 VixDiskLib_Shrink 函数能够回收 VMDK File 中未被使用的空间(标记为 0 的 Blocks),该函数一般对 SPARSE 稀疏型磁盘文件而言效果更佳明显。同样的该函数仅支持 hosted disk。

NOTE:我们可以调用 VixDiskLib_Write 来将未被使用的磁盘空间置零,然后再调用 VixDiskLib_Shrink 来回收这些空间。而且调用 VixDiskLib_Shrink 并不会减少磁盘的实际容量,却可以获得更多的可用空余磁盘空间。

VixDiskLib_Defragment 整理 VMDK File

函数原型

/**
* Defragments an existing disk.
* @param diskHandle [in] Handle to an open virtual disk.
* @param progressFunc [in] Callback to report progress (called on the same thread).
* @param progressCallbackData [in] Opaque pointer passed along with the percent
* complete.
* @return VIX_OK if success, suitable VIX error code otherwise.
*/
VixError
VixDiskLib_Defragment(VixDiskLibHandle diskHandle,
VixDiskLibProgressFunc progressFunc,
void *progressCallbackData);

函数调用

vixError = VixDiskLib_Defragment(diskHandle, DefragProgressFunc, NULL);

调用函数 VixDiskLib_Defragment 能够对一个已存在的 VMDK File 进行碎片整理,碎片整理能够有效的提高磁盘的性能。需要注意的是碎片整理操作仅会对 SPARSE 稀疏型磁盘文件有效,而平面类型 FLAG 的磁盘文件则无需要进行碎片整理。

NOTE:VixDiskLib_Defragment 实际上是将 2GB 范围内的碎片数据往更低的区段移动,该操作对于 GuestOS 自带的碎片整理工具而言是透明的。VMware 会推荐一种「由内到外」的碎片整理方案,即使用 GuestOS ==> VMDK(VixDiskLib_Defragment) ==> HostOS 的顺序进行整理。

VixDiskLib_Rename 重命名 VMDK File

函数原型

/**
* Renames a virtual disk.
* @param srcFileName [in] Virtual disk file to rename.
* @param dstFileName [in] New name for the virtual disk.
* @return VIX_OK if success, suitable VIX error code otherwise.
*/
VixError
VixDiskLib_Rename(const char *srcFileName,
const char *dstFileName);

函数调用

vixError = VixDiskLib_Rename(oldDiskpath, newDiskpath);

该函数需要传入两个参数,新旧的 VMDK File Name。HostOS 需要将 GuestOS 的 VMDK File 储存在一个可预见的位置,在 Rename 期间的任何文件访问都有可能造成 I/O 失败,或导致 GuestOS 故障。所以需要注意的是,调用该函数之前,需要确保关闭虚拟机电源。

VMware 虚拟机的快照

通常,我们可以通过创建一个虚拟机的快照来生成 VMDK File 的一个重写日志,这个快照包含了磁盘数据和虚拟机状态。但对于 Hosted Disk,我们也可以直接调用 VixDiskLib_CreateChild 函数来生成一个重写日志。所以在 VDDK 相关的术语中,子磁盘(Child Disk)、重做日志(Redo Log)、差异链(Delta Link)、快照数据文件(Snapshot File) 具有相同的含义。从最原始的父磁盘文件(Base File)开始,每一个子磁盘都包含了从父磁盘的原始状态到自身当前状态的集合。换句话说,每个快照数据文件都包含了从上一个快照时间点到当前快照时间点之间的更新数据。

VMware 虚拟化编程(7) — VixDiskLib 虚拟磁盘库详解之三

NOTE 1:这些快照数据文件类型为 VIXDISKLIB_DISK_VMFS_SPARSE 稀疏型磁盘文件,并使用了 COW 的机制来存放更新数据,以此来节省存储空间。可参考 《再谈 COW、ROW 快照技术》

NOTE 2:子磁盘被创建之后,vm.vmdk 的指向就会被更改指向它。也就是说,此时的父磁盘是 OR 只读的状态,而子磁盘才是 RW 可读写的状态。

NOTE 3:VMware 虚拟机的第一次快照所得到的快照数据实际上是虚拟机当前状态的全量数据,而之后的快照一般均为增量数据。

VixDiskLib_CreateChild 创建 VMDK File 的快照文件

函数原型

/**
* Creates a redo log from a parent disk.
* @param diskHandle [in] Handle to an open virtual disk.
* @param childPath [in] Redo log file name given as absolute path
* e.g. "c:\\My Virtual Machines\\MailServer\SystemDisk_s0001.vmdk".
* @param diskType [in] Either VIXDISKLIB_DISK_MONOLITHIC_SPARSE or
* VIXDISKLIB_DISK_SPLIT_SPARSE.
* @param progressFunc [in] Callback to report progress.
* @param progressCallbackData [in] Callback data pointer.
* @return VIX_OK if success, suitable VIX error code otherwise.
*/
VixError
VixDiskLib_CreateChild(VixDiskLibHandle diskHandle,
const char *childPath,
VixDiskLibDiskType diskType,
VixDiskLibProgressFunc progressFunc,
void *progressCallbackData);

函数调用

vixError = VixDiskLib_CreateChild(parentHandle, diskPath, VIXDISKLIB_DISK_MONOLITHIC_SPARSE, NULL, NULL);

VixDiskLib_Attach 读取指定快照文件的数据

函数原型

/**
* Attaches the child disk chain to the parent disk chain. Parent handle is
* invalid after attaching and child represents the combined disk chain.
* @param parent [in] Handle to the disk to be attached.
* @param child [in] Handle to the disk to attach.
* @return VIX_OK if success, suitable VIX error code otherwise.
*/
VixError
VixDiskLib_Attach(VixDiskLibHandle parent, VixDiskLibHandle child);

函数调用

vixError = VixDiskLib_Attach(parentHandle, childHandle);

VMware 虚拟化编程(7) — VixDiskLib 虚拟磁盘库详解之三

如上图,当我们希望访问 Child1 时间点的快照数据时,需要先将子磁盘 Child1a 挂载到 Child1 之后,那么实际上我们会获取到了 Child1 快照时间点时刻的虚拟机状态。类似于将虚拟机恢复到指定的快照时间点,也类似于新建了一条快照链的分支。

保持磁盘操作的原子性

当我们对虚拟机的虚拟磁盘进行操作时,需要对这些操作保证一定的原子性,也就是说我们需要保证在对虚拟磁盘的才做完成之前,不会被别的虚拟机操作打断。VDDK 5.0 之后包含了两个新的 VixDiskLib 调用 PrepareForAccess 和 EndAccess,它们用于虚拟机备份时禁用和启用存储迁移功能。这避免了在执行备份时,虚拟机移动了它的存储,从而导致旧的磁盘镜像被遗留了下来。VMware 强烈建议使用这两个调用。

VixDiskLib_PrepareForAccess 推迟干扰虚拟磁盘访问的虚拟机操作

函数原型

/**
* This function is used to notify the host of the virtual machine that the
* disks of the virtual machine will be opened. The host disables operations on
* the virtual machine that may be adversely affected if they are performed
* while the disks are open by a third party application.
*
* @param connectParams [in] This is always used on remote connections.
* @param identity [in] An arbitrary string containing the identity of the
* application.
* @return VIX_OK if success, suitable VIX error code otherwise.
*/
VixError
VixDiskLib_PrepareForAccess(const VixDiskLibConnectParams *connectParams,
const char *identity);

函数调用

vixError = VixDiskLib_PrepareForAccess(&cnxParams, “vmName”);
  • @param identity:一个标识字符串,仅用于跟踪目的,且长度不超过 50 个字符。可以是虚拟机名称或者应用程序名称。

VixDiskLib_PrepareForAccess 函数会通知 vCenter 服务器,ESXi 主机正在打开一个虚拟磁盘,ESXi 主机应该推迟所有可能会干扰这次虚拟磁盘访问的虚拟机操作。例如:该函数会禁止虚拟机的 RelocateVM_Task 迁移操作。

NOTE 1:在备份应用程序中,应该在创建虚拟机快照前调用此函数。

NOTE 2:调用该函数必须连接到 vCenter,如果直接到 ESX/ESXi Host 上调用,系统会抛出错误消息:「VDDK: HostAgent is not a vCenter, cannot disable svmotion.

NOTE 3:在调用该函数时应该以超时时间作为释放依据,否则容易出现程序逻辑上的流动。EXAMPLE:下面的代码片段中,在备份程序中使用 PrepareForAccess 等待存储迁移(Storage vMotion)完成,设定最多只等待 10 分钟。

if(appGlobals.vmxSpec != NULL) {
for (int i = 0; i < 10; i+) {
vixError =VxiDiskLib_PrepareForAccess(&cnxParams, “Sample”);
if (vixError == VIX_OK) {
break;
}
else {
Sleep(60000);
}
}
}

VixDiskLib_EndAccess 结束访问占用

函数原型

/**
* This function is used to notify the host of a virtual machine that the
* virtual machine disks are closed and that the operations which rely on the
* virtual machine disks to be closed can now be allowed.
*
* @param connectParams [in] Always used for a remote connection. Must be the
* same parameters as used in the corresponding PrepareForAccess call.
* @param identity [in] An arbitrary string containing the identity of the
* application.
* @return VIX_OK of success, suitable VIX error code otherwise.
*/
VixError
VixDiskLib_EndAccess(const VixDiskLibConnectParams *connectParams,
const char *identity);

函数调用

vixError= VixDiskLib_EndAccess(&cnxParams, “vmName”);

每一次调用 VixDiskLib_PrepareForAcccess 都应该相应的调用一次 VixDiskLib_EndAccess 函数。该函数会通知 ESXi 主机,虚拟磁盘访问已经被关闭,被推迟的虚拟磁盘操作现在可以启用了,如 vMotion,RelocateVM_Task。

NOTE:该函数还能够在关闭所有虚拟磁盘并删除所有虚拟机快照后调用,也能够在奔溃后调用它来做一次 Cleanup 清除操作。

Managed Disk 操作需知

  • 如果使用 VixDiskLib_Connect 打开一个 Managed Disk 的连接,必须提供有效的 vSphere 访问认证信息。
  • 在 ESX/ESXi Host 上,VixDiskLib_Open 不能打开磁盘链中的单个连接。
  • 如果要 在 ESX/ESXi Host 上创建一个 Managed Disk,首先需要调用 VixDiskLib_Create 创建一个 Hosted Disk,然后使用 VixDiskLib_Clone 将 Hosted Disk 转换为 Managed Disk。
  • VixDiskLib_Defragment 只能针对 Hosted Disk 进行碎片整理。
  • VixDiskLib_Grow() 只能扩展 Hosted Disk。
  • VixDiskLib_Unlink() 只能删除 Hosted Disk。
  • ESXi 5.1 之前,HotAdd 传输只能用于 vSphere 企业版或更高版本。

Hosted Disk 操作需知

除了高级传输模式以外,大多数操作都 能支持 Hosted Disk,但不包括:

- VixDiskLib_ConnectEx() 扩展连接函数。

- SAN 和 HotAdd 高级传输。

- 使用 VixDiskLib_PrepareForAccess() 和 VixDiskLib_EndAccess() 延迟存储迁移。

远程创建一个虚拟机的算法

  • 使用 VixDiskLib_Create 创建一个 2GB 大小的本地 VMDK File。
  • 使用 VixDiskLib_Write 将 GuestOS 的镜像和应用软件写入 VMDK File。
  • 将本地的 VMDK File 克隆到 ESX/ESXi Host 的 VMFS 文件系统上。
vixError= VixDiskLib_Clone(appGlobals.connection, appGlobals.diskPath, srcConnection,appGlobals.srcPath, &createParam, CloneProgressFunc, NULL, TRUE);
# appGlloobals.connection, appGlobals.diskPath 表示 ESX/ESXi Host 上的远程 VMDK File
# srcConection, appGlobals.srcPath 表示本地 VMDK File。
  • 打开新的 GuestOS,得到一个新的虚拟机。

NOTE:无论在步骤 1 中创建什么类型的 VMDK File,在步骤 3 克隆之后它都会变成 VIXDISKLIB_DISK_VMFS_FLAT 类型的虚拟磁盘。

回滚虚拟机到指定快照时间点上的算法

  • 找到指定快照时间点的 <vmname>-<NNN>.vmdk 重写日志。<NNN> 是一个序列号,可以通过时间戳来表示。
  • VixDiskLib_InitEx 初始化虚拟磁盘库
  • VixDiskLib_ConnectEx 连接到虚拟机
  • VixDiskLib_Open 打开重写日志,并得到它的父句柄。
  • VixDiskLib_Create 创建一个子磁盘
  • VixDiskLib_Attach 将子磁盘附加到指定快照时间点对应的磁盘上
  • 读、写附加后的子虚拟磁盘。