4 虚拟磁盘API函数

本章提供的虚拟磁盘API的函数概述。在按字符顺序介绍的API函数之后,将会重点关注每个函数如何使用,和它们在程序中的使用顺序一致(除了高级传输函数SANHotAdd在关闭函数以后介绍)

虚拟磁盘库函数

可以在VMDK安装目录的doc子目录中找到index.html文件,使用Web浏览器打开它就可以查看VixDiskLib的接口参考文档。在大多数参考手册中,函数通过字母顺序进行组织,但是在本章节中,函数通过它们如何被调用来组织。

当文档中提到一个函数仅支持主机磁盘时,它意味着VMware工作站或其他类似产品上的虚拟磁盘镜像。存放在VMFS分区上由ESX/ESXivCenter服务器管理的虚拟磁盘被称为托管磁盘“(managed disk)

本节讨论的函数基于第三章中的概念和相关数据结构。

如果需要访问VMFS上虚拟磁盘,I/O操作默认情况下都需要通过ESX/ESXi主机完成,它们管理物理磁盘存储。如果要使用直接访问SAN存储的函数调用,开始时就需要调用VixDiskLib_ConnectEx()函数,正如高级传输接口中描述的那样。

函数概述

4-1中按字母顺序列出了虚拟磁盘API中的函数。

4-1. 虚拟磁盘库函数

函数 描述
VixDiskLib_Attach 将子磁盘链附加到父磁盘链上。
VixDiskLib_Cleanup 清除遗留的传输信息。
VixDiskLib_Clone 将虚拟磁盘拷贝到指定目标,并转换成适当的格式。
VixDiskLib_Close 关闭一个打开的虚拟磁盘。
VixDiskLib_Connect 连接到虚拟磁盘库以获取相关服务。
VixDiskLib_ConnectEx 连接到高级的传输。
VixDiskLib_Create 根据指定参数创建虚拟磁盘文件。
VixDiskLib_CreateChild 针对主机磁盘创建一个子磁盘(重做日志或差异磁盘)
VixDiskLib_Defragment 对虚拟磁盘的扇区进行碎片整理。
VixDiskLib_Disconnect 断开虚拟磁盘库的连接
VixDiskLib_EndAccess 通知主机可能需要迁移一个虚拟机。
VixDiskLib_Exit 释放库占用的所有资源。
VixDiskLib_FreeErrorText 释放GetErrorText获得的消息缓存。
VixDiskLib_FreeInfo 释放GetInfo分配的缓存。
VixDiskLib_GetErrorText 获取错误码的文字描述。
VixDiskLib_GetInfo 获取虚拟磁盘的信息。
VixDiskLib_GetMetadataKeys 获取虚拟磁盘元数据中的所有键。
VixDiskLib_GetTransportMode 获取当前的传世模式。
VixDiskLib_Grow 扩展已有虚拟磁盘的大小。
VixDiskLib_Init 初始化老的虚拟磁盘库,已经被InitEx代替。
VixDiskLib_InitEx 初始化新的虚拟磁盘库。
VixDiskLib_ListTransportModes 获取可用的传输模式。
VixDiskLib_Open 打开虚拟磁盘。
VixDiskLib_PrepareForAccess 通知主机避免迁移虚拟机。
VixDiskLib_Read 从打开的虚拟磁盘上读取数据。
VixDiskLib_ReadMetadata 从虚拟磁盘的元数据中读取指定键的值。
VixDiskLib_Rename 重新命名虚拟磁盘。
VixDiskLib_Shrik 从虚拟磁盘中回收空的扇区。
VixDiskLib_SpaceNeededForClone 计算克隆一个虚拟磁盘需要的空间大小(字节)
VixDiskLib_Unlink 删除指定的虚拟磁盘。
VixDiskLib_Write 向打开的虚拟磁盘中写入数据。
VixDiskLib_WriteMetadata 使用键/值对更新虚拟磁盘的元数据。

启动

VixDiskLib_Init()以及VixDiskLib_Connect()函数在所有的虚拟磁盘程序中都应该存在。VixDiskLib_Init()已经被VixDiskLib_InitEx()代替。

初始化库

VixDiskLib_Init()初始化就的虚拟磁盘库,参数中的majorVersion以及minorVersion表示VDDK的发行版本。第三、四、五个参数知名日志、警告、错误的处理函数接口。DLL文件以及共享对象存放在libDir参数指定的目录中。

VixErrorvixError = VixDiskLib_Init(majorVer, minorVer, &logFunc, &warnFunc,&panicFunc, libDir);

由于国际化的限制,在程序开始时每个进程中只能调用一次VixDiskLib_Init()函数(You should callVixDiskLib_Init() only once per process because of internationalizationrestrictions, at the beginning of your program)。应该在程序结束时调用VixDiskLib_Exit()来清除各种资源。对于多线程程序,你需要编写自己的logFunc函数,应该默认的函数并不是线程安全的。

大部分情况下,你都需要使用VixDiskLib_InitEx()代替VixDiskLib_Init(),它允许你指定配置文件。

连接工作站或服务器

VixDiskLib_Connect()将库连接到本地VMware主机或者远程服务器。对于本地系统的主机磁盘,大部分连接参数都田null值。对于ESX/ESXi主机上的托管磁盘,需要指定虚拟机的名称,ESX/ESXi主机的名称,用户名,密码,以及端口。

vixError= VixDiskLib_Connect(&cnxParams, &srcConnection);

可以使用VixDiskLibSSPICreds连接参数来启用SSPI(Security Support ProviderInterface)认证。SSPI的优点是不用再配置文件或注册表中使用明文存储密码。如果要使用SSPI,需要满足下面的条件

  • 必须之间连接到vSphere服务器或VirutalCenter2.5或更新版本的服务器。

  • 应用程序和连接必须使用两种账户之一,使用以下两种方式建立连接:

使用和代理以及vSphere服务器上的用户名/密码认证相同的用户上下文。

或者使用域用户。应用程序尝试使用本地系统账户上下文进行连接将会失败。

  • 用户上下文在代理上必须具有管理员权限,并且属于vSphereVirtualCenter分配的VCB备份角色。

如果启动符合上述条件,就可以通过将USERNAME设置到__sspi__来启用SSPI认证。对于SSPI,密码必须设置,但是被忽略,可以设置为空(“”null)

在结束程序前总是要调用VixDiskLib_Disconnect()

VMX说明

VMware平台的产品中,.vmx是一个文本文件,通常存放在和虚拟磁盘磁盘文件相同的目录中,保存虚拟机的配置。虚拟机可执行(Virtual MachineeXecutalbe, VMX)进程是虚拟机在用户空间的一个组件。虚拟磁盘库通过VMX进程连接到虚拟机存储。

当指定连接参数时,推荐的vmxSpec的语法如下:

  • 虚拟机的托管对象引用(managed object reference),可以在程序中使用PropertyCollector托管对象获得的一个封装(opaque)对象:

moRef=

ESX/ESXi主机上的虚拟机或磁盘快照的moRef,通常和vCenter服务器管理的虚拟机或磁盘快照的moRef不同。下面是两个moRef,一个是ESXi,一个是vCenter服务器,引用的都是同一个快照。

moRef= 153

moRef= 271

磁盘操作

这些函数创建,打开,读,写,查询,关闭虚拟磁盘。

创建新的主机磁盘(Hosted Disk)

连接到主机后,就可以使用VixDiskLib_Create()在本地创建一个新的虚拟磁盘。在createParams参数中,需要指明磁盘类型,硬件版本,扇区的容量。这个函数支持主机磁盘。对于托管磁盘,首先需要创建一个主机类型的虚拟磁盘,然后使用VixDiskLib_Clone()将虚拟磁盘转换为托管类型。

vixErr= VixDiskLib_Create(appGlobals.connection, appGlobals.diskPath,&createParams, NULL, NULL);

FAT32以及FAT文件系统上,VixDiskLib_Create()要求虚拟磁盘的大小不能超过4GBNTFS文件系统上,虚拟磁盘的大小不超过16TB-54KB,在ReFSexFAT文件系统,不能超过264-1。在最新的vSphere5.5以及更新版本中,将支持大于2TBVDDK文件。

包括NFS3在内的基于POSIX的文件系统不再有2GB的文件大小限制。尽管创建磁盘时采取了多种检查以避免超大文件,但是用户还是需要自行设置NFS2Linux2.4(EFS)上的2GB大小限制。

打开本地或远程磁盘

连接到工作站或服务器后,VixDiskLib_Open()就可以打开一个虚拟磁盘了。如果使用SANHotAdd传输模式,需要有一个存在的快照才能打开远程磁盘进行写操作。

vixError= VixDiskLib_Open(appGlobals.connection, appGlobals.diskPath,appGlobals.openFlags, &strHandle);

可以指定以下标志:

VIXDISKLIB_FLAG_OPEN_UNBUFFERED禁用主机磁盘缓存。

VIXDISKLIB_FLAG_OPEN_SINGLE_LINK针对主机磁盘,打开当前的磁盘链接,而不是真个磁盘链。

VIXDISKLIB_FLAG_OPEN_READ_ONLY只读模式打开虚拟磁盘。

读磁盘扇区

通过指定开始扇区和扇区个数,VixDiskLib_Read()从打开的虚拟磁盘中读取一片连续的扇区。扇区的大小可以不一样,但是文件中已经定义为512个字节,因为VMDK文件使用这个扇区大小。

vixError= VixDiskLib_Read(srcHandle, i, j, buf);

写磁盘扇区

VixDiskLib_Write()向打开的磁盘中写入一个或多个扇区。这个函数的第4个参数buf,长度必须是VIXDISKLIB_SECTOR_SIZE的整数倍字节。

vixError= VixDiskLib_Write(srcHandle, i, j, buf);

关闭本地或远程磁盘

VixDiskLib_Close()关闭打开的虚拟磁盘。

VixDiskLib_Close(srcHandle);

获取磁盘信息

vixError= VixDiskLib_GetInfo(srcHandle, diskInfo);

VixDiskLib_GetInfo()获取打开的虚拟磁盘的相关信息,分配并填充VixDiskLibDiskInfo结构,其中一部分信息和元数据相同。

释放磁盘信息

这个函数释放由VixDiskLib_GetInfo()分配的内存,避免内存泄漏。

vixError= VixDiskLib_FreeInfo(diskInfo);

错误处理

这些函数对于处理错误信息很有用。

获取错误描述

VixDiskLib_GetErrorText()返回一个数字错误码代表的错误的文字描述。

char*msg = VixDiskLib_GetErrorText(errorCode, NULL);

释放错误描述

VixDiskLib_FreeErrorText()释放保存错误描述信息的内存缓冲区。

VixDiskLib_FreeErrorText(msg);

元数据(Metadata)

VMware提供了一套机制来处理虚拟磁盘的元数据,但是它很少被使用。

读取元数据键

VixDiskLib_ReadMetadataKeys()从虚拟磁盘的元数据中获取所有存在的键,而不是键值。这个函数需要和VixDiskLib_ReadMetadata()配合使用。

vixError= VixDiskLib_ReadMetadataKeys(disk.Handle(), &buf[0], requiredLen, NULL);

读取元数据值

vixError= VixDiskLib_ReadMetadata(disk.Handle(), appGlobals.metakey, &val[0],requiredLen, NULL);

从磁盘元数据中获取指定键的值。本地VMDK的元数据和ESX/ESXi主机上的托管磁盘的元数据并不相同。VMFS元数据保存在一个映射文件中,包括的信息由磁盘卷标、LUN或分区布局、链接个数、文件属性、锁等。元数据还描述了可用的裸磁盘映射的存储封装(encapsulation of rawdisk mapping, RDM)

下面是一个简单的元数据表。Uuid表示虚拟磁盘的全局唯一标识符。

adapterType= buslogic

geometry.sectors= 32

geometry.heads= 64

geometry.cylinders= 100

uuid= 60 00 c2 93 7b a0 3a 03 9f 22 56 c5 29 93 b7 27

更改元数据

VixDiskLib_WriteMetadata()通过指定的键值对来更新虚拟磁盘的元数据。如果键值对不存在,则会新增一项,如果键存在,值将会更新。键可以被设置为空,但是不能删除。

vixError= VixDiskLib_WriteMetadata(disk.Handle(), appGlobals.metakey,appGlobals.metaVal);

克隆虚拟磁盘

计算克隆需要的空间

下面的函数计算在将磁盘转换为可能的格式后,克隆这个虚拟磁盘所需要的空间大小,以字节为单位:

vixError= VixDiskLib_SpaceNeededForClone(child.Handle(), VIXDISKLIB_DISK_VMFS_FLAT,&spaceReq);

注意:VixDiskLib_SpaceNeededForClone()可能不会返回正确的结果,如当使用老的磁盘类型VIXDISKLIB_DISK_VMFS_THIN时会返回VIX_E_INVALID_ARG

拷贝数据克隆磁盘

这个函数将数据从一个虚拟磁盘拷贝到另一个虚拟磁盘,并转换相应的参数(磁盘类型、大小、硬件)

vixError= VixDiskLib_Clone(appGlobals.connection, appGlobals.diskPath, srcConnection,appGlobals.srcPath, &createParam, CloneProgressFunc, NULL, TRUE);

磁盘链和重做日志(Disk Chain and Redo Logs)

VDDK相关的术语中,下面这些词都具有相同含义:子磁盘(child disk),重做日志(redo log),差异链(delta link)。从最原始的父磁盘开始,每一个子磁盘都构成了一个从虚拟磁盘的原始状态到当前状态的重做日志,且每一个时间点只有一个链接(From the originalparent disk, each child constitutes a redo log pointing back from the presentstate of the virtual disk, one step at a time, to the original.)。下面这个伪方程式表现了备份和快照的相对复杂度:

Backupp_w_picpath < child disk = redo log = delta link < snapshot

一个备份镜像(例如存放在磁带上的)小于一个子磁盘,因为备份镜像仅仅是一个数据流。一个快照大于一个子磁盘,因为快照还包含了虚拟机的状态,以及VMDK中的文件系统状态。

从父磁盘创建子磁盘

通常你从父磁盘创建第一个子磁盘,然后从这个磁盘链的上一个子磁盘创建更多的子磁盘。如图4-1所示,这些磁盘通过稀疏(SPRSE)格式,记录从开始到现在发生改变过的额磁盘扇区。

VMware虚拟磁盘编程指导(四)_第1张图片

VixDiskLib_CreateChild()为一个本地磁盘创建一个子磁盘(或者重做日志)。创建子磁盘后,通常不需要打开父子磁盘或者磁盘链中更早的磁盘。子磁盘的vm.vmdk文件指向重做日志,而不是父磁盘,如本例当中的vm-flag.vmdk。要访问原始的父磁盘,或更早的子磁盘,可以针对本地磁盘调用VixDiskLib_Attach()

vixError= VixDiskLib_CreateChild(parent.Handle(), appGlobals.diskPath,VIXDISKLIB_DISK_MONOLITHIC_SPARSE, NULL, NULL);

将子磁盘附加到父磁盘

VixDiskLib_Attach()将子磁盘附件到它的父磁盘链中。之后,父磁盘的句柄将失效,子磁盘句柄将表示结合之后的重做日志的磁盘链(the child handlerepresents the combined disk chain of the redo logs)

vixError= VixDiskLib_Attach(parent.Handle(), child.Handle());

假如你需要访问Child1记录的旧的磁盘镜像,将心的Child1a的句柄附加到Child1,它能够提供Child1a的父句柄(Attach the handle ofnew Child1a to Child1, which provides Child1a’s parent handle),如图4-2所示。现在就可以对Child1a虚拟磁盘进行打开、读、写操作。VMware虚拟磁盘编程指导(四)_第2张图片

-子磁盘链能够有效的利用存储空间,因为子VMDK仅仅记录从上一次调用VixDiskLib_CreateChild()以来发生改变的扇区。父-子磁盘链同样提供了一种重做机制,允许在程序中通过VixDiskLib_Attach()访问任何时间点的虚拟磁盘(access to any generation)

打开磁盘链

对于基准磁盘B,以及子磁盘C0C1C2,打开C2可以获得B+C0+C1+C2的全部内容,而不是链接的磁盘扇区(not really addition linked data sectors)。打开C1则会获得B+C0+C1的内容。

比记录基准磁盘以及哪个子磁盘是哪个磁盘的后裔更好的解决方案是采用修改块跟踪(changed blocktracking)vShpere接口中有QueryChangedDiskAreas()函数。

托管磁盘的重做日志(Redo Logs on Managed Disk)

对于vShpere上的托管虚拟磁盘,快照主要用来保存系统状态和进行备份操作,而克隆链接则是为了View桌面的配置而创建的客户镜像(while linked clones are create guestp_w_picpaths for provisioning of View desktops)。快照通常是一个父子磁盘链中某一个重做日志,而克隆链接通常包含基于同样父磁盘的多个重做日志。

vSphere5.5中,克隆链接架构的修改,提供了备份还原的效率(the handling oflinked clone hierarchies was changed to improve the efficiency of backup andrestore)。磁盘对象包含了一个“磁盘置备(disk backing)”,它包括一直到达基准磁盘的一个或多个父置备对象(contains one or moreparent backing objects until the base disk is reached)。这允许我们访问父子磁盘链中任意位置。

对于一个干净的从未使用过的基准虚拟机而言,克隆链接结构或者快照链总是包含正确数量的链中节点的父置备对象(the linked clonehierarchy or snapshot chain always has the proper number of parent backingobjects for the nodes in the chain)

VDDK没有任何方便的方法,能够备份或还原克隆链结构以及快照链。备份程序如果要支持这个特点,就需要自己查找并保存这个结构。

如果基准磁盘或子磁盘有一个额外的快照,或者被用来创建克隆链接的重做日志没有被删除,或者任何父或子磁盘需要磁盘合并或处在非法的快照状态,就可能有额外(很多)的父置备对象。(When the base disk or a childdisk has an extra snapshot, when redo logs used to create linked clones werenever deleted, or when any parent or child disk in the chain needs diskconsolidation or is in bad snapshot state, it is possible to have extra(toomany) parent backing objects.)

还原应用绝不应该假设父置备对象的个数(restore applicationsshould never assume the correct number of parent backing objects)。它们应该递归查询,直到找到基准父置备对象为止,并确定正确的父置备队形匹配的子节点何时还原(make sure whenrestoring leaf nodes that the correct parent backing object matches the nodebeing restored)

管理磁盘操作

这些函数重命名、扩展、整理、压缩、删除虚拟磁盘。

重命名磁盘

VixDiskLib_Rename()修改一个虚拟磁盘的名称。只能在虚拟机关闭时调用此函数。

vixError= VixDiskLib_Rename(oldGlobals.diskpath, newGlobals.diskpath);

扩展本地磁盘

VixDiskLib_Grow()通过增加扇区扩展一个已存在的虚拟磁盘。这个函数只支持本地磁盘,不支持托管磁盘。

vixError= VixDiskLib_Grow(appGlobals.connection, appGlobals.diskpath, size, FALSE,GrowProgressFunc, NULL);

磁盘整理

VixDiskLib_Defragment()对一个已存在的虚拟磁盘进行碎片整理。碎片整理对稀疏类型(SPARSE)的文件有效,对平面类型(FLAG)的磁盘不做任何事情,这个函数直接返回VIX_OK。这个函数支持本地磁盘,而不支持托管磁盘。

vixError= VixDiskLib_Defragment(disk.Handle(), DefragProgressFunc, NULL);

磁盘整理合并2GB范围内的数据,将数据迁移到更低的范围内(Defragment consolidatesdata in 2GB extents, moving data to lower-numbered extents)。它和客户机系统内的磁盘整理工具是不相关的,如Windows上磁盘>属性>工具>碎片整理,以及Linux Ext2文件系统的defrag命令行。

VMware推荐由里到外的磁盘整理:首先使用虚拟机进行磁盘整理,然后使用这个函数或者VMware的整理工具,最后使用宿主操作系统。

压缩磁盘

VixDiskLib_Shrik()从一个虚拟磁盘中回收没有使用的空间,未使用空间就是被标记为0的块。比起提前分配的FLAT类型文件,这对SPARSE类型文件更有效,能够获得更多空间。成功时这个函数会返回VIX_OK。这个函数支持本地磁盘,但不支持托管磁盘。

vixError= VixDiskLib_Shrink(disk.Handle(), ShrinkProgressFunc, NULL);

VMware系统工具中,“prepare”清空VMDK中未使用的块使得“shrink”能够回收它们。使用VixDiskLib_Write()清空未使用的块,并使用VixDiskLib_Shrink()来回收空间。压缩并不改变虚拟磁盘的容量,但是使得更多空间可用。

删除磁盘

VixDiskLib_Unlink()删除指定虚拟磁盘的所有空间,删除磁盘数据。这和命令行中的删除或擦除类似。

vixError= VixDiskLib(appGlobals.connection, appGlobals.diskpath);

关闭

所有的虚拟磁盘接口应用程序在程序结尾都应该调用这些函数。

断开服务器连接

VixDiskLib_Disconnect()断开已有连接。

VixDiskLib_Disconnect(srcConnection);

清空并退出

VixDiskLib_Exit()在退出前清空库资源。

VixDiskLib_Exit();

高级传输API

针对托管磁盘,VDDK的第一个版本要求到ESX/ESXi主机的网络访问(LAN或者NBD传输)。从VDDK1.1开始,程序可以直接访问存储设备上的虚拟磁盘,而不需要LAN。直接的SAN访问提高了I/O性能。有一系列的API可以用于选择最有效的传输方法,包括:

VixDiskLib_InitEx()—— 初始化高级传输库。你必须指定库的位置。在你的应用中替换掉VixDiskLib_Init()

VixDiskLib_ListTransportModes()—— 枚举出虚拟函数库支持的传输方法。

VixDiskLib_ConnectEx()—— 使用你选择的或可用的最佳传输模式建立连接,访问虚拟磁盘。当前它并不检查传输类型的有效性。在你的应用程序中替换掉VixDiskLib_Connect()

初始化虚拟磁盘API

VixDiskLib_InitEx()代替VixDiskLib_Init()初始化新版本的库。参数很简单,你需要指定真实的libDir以及新的configFile参数。对于多线程程序,你需要编写自己的logFunc,因为默认的日志函数不是线程安全的。在Windows上,libDir可能是C:\Program Files\VMware\VMwareVirtual Disk Development Kit,在Linux上,libDir可能是/usr/lib/vmware-vix-disklib

VixErrorvixError = VixDiskLib_InitEx(majorVersion, minorVersion, &logFunc, &warnFunc,&panicFunc, *libDir, *configFile);

VDDK和大多数其他VMware产品中,默认情况下,日志消息会保存在临时目录或日志目录中。

下面列出了一些configFile中当前支持的内容。指定配置的正确方式是name=value,可查看示例4-1中的配置文件例子。

  • tempDirectory =“

  • vixDiskLib.transport.LogLevel重写vixDiskLib传输(不包括NFC)函数的默认日志级别。默认的值是3,有效范围是066最详细,0不做任何记录。

  • vixDiskLib.disklib.EnableCachevixDiskLib的缓存默认是关闭的。设置为1可以打开缓存。当信息被重复读取或随机访问时,缓存可以提高性能。备份程序中,信息通常是被连续读取,缓存实际上降低了性能。如果使用缓存,当磁盘扇区在刷新缓存之前被其他应用程序重复写入,备份程序获取状态信息就有风险。

  • vixDiskLib.linuxSSL.verifyCertificates连接到Linux虚拟机时是否检查SSL指纹。0表示关闭,1表示打开,默认值是0

超时值存储在32位的数值中,所以你能指定的最大超时值是2G(2,147,483,648)。超时值是以毫秒为单位的,应用于每个磁盘句柄。NFC设置应用于NBD/NBDSSL,但不适用于SANHotAdd

下面NFC相关的选线会覆盖多个NFC函数的默认值。示例4-1中的NFC超时对应于ESXi5.x主机上的默认值。

  • vixDiskLib.nfc.AcceptTimeoutMs覆盖NFC接收操作的默认值(3分钟)

  • vixDiskLib.nfc.RequestTimeoutMs覆盖NFC请求操作的默认值(3分钟)

  • vixDiskLib.nfc.ReadTimeoutMs覆盖NFC读操作的默认值(1分钟)

  • vixDiskLib.nfc.WriteTimeoutMs覆盖NFC写操作的默认值(10分钟)

  • vixDiskLib.nfcFsrvr.TimoutMs覆盖NFC文件系统操作的默认值(0,不定时等待)。如果你指定了一个值,如果文件系统在给定的时间内空闲就会发生超时。使用默认值的风险是,在罕见的灾难性通信故障时,文件系统将会保持锁定。

  • vixDiskLib.nfcF***vrWrite.TimeoutMs覆盖NFC文件系统写操作的默认值(没有超时),以毫秒为单位。如果你指定一个值,当写操作在指定时间间隔内没有成功完成的话,就会发生超时。

  • vixDiskLib.nfc.LogLevel覆盖NFC操作的默认日志级别。默认值是1,表示仅仅记录错误消息。各个值得含义如下。每一级都包含更低一级的日志。这是NFC的最终设置。

0= 静默(最小日志)

1= 错误

2= 警告

3= 信息

4= 调试

VMware虚拟磁盘编程指导(四)_第3张图片

日志文件位置

Linux系统上,日志消息会默认保存在/var/log中。Windows上,它们默认保存在临时目录中,这个位置可能随时发生变化。早期的Windows系统使用C:\Windows\TempWindows XP以及2003使用C:\Documents and Settings\\LocalSettings\Temp\vmware-VistaWindows72008则使用C:\Users\\AppData\Local\Temp\vmware-

在所有版本的Windows系统上,TEMP环境变量重写了默认的临时文件夹位置。“临时”实际上是名不副实的,因为临时文件夹中从来也没有被删除,除非用户或应用程序主动删除它们。如果TEMP或者Windows默认临时文件夹不存在,VDDK或其他VMware软件将会使用\Temp文件夹。

如示例4-1所示,你也可以指定自己的临时文件目录。

枚举可用的传输方法

VixDiskLib_ListTransportModes()函数返回一个以冒号分隔的字符串值,表示当前支持的传输方法,如“file:san:hotadd:nbd”nbd表示LAN传输。SSL加密NBD传输可用时显示为nbdssl

printf(“Transportmethods: %s\n”, VixDiskLib_ListTransportModes());

如果所有的网络传输模式都可用的话,默认的排序是 san:hotadd:nbdssl:nbd

使用直接模式的Linux SAN模式(SAN Mode on Linux Uses Direct Mode)

Linux上的SAN传输,读、写操作都使用直接(direct)”模式(O_DIRECT),意味着读、写都没有使用任何缓存。直接模式阻止其他进程访问最新数据,避免提交写缓存前由于进程终止造成的信息丢失。如果应用遵循以下指导进行读写操作,就会达到最佳的效率:

SAN中执行操作的数据偏移应该是页大小(4096)的整数倍。

数据传输的缓存应该是页边界对齐的。

传输的长度应该是页大小的整数倍。

连接到VMware vShpere

VixDiskLib_ConnectEx()将库连接到远程ESX/ESXi主机或者VMware vCenter服务器上的托管磁盘。对于本地系统上虚拟磁盘,它的作用和VixDiskLib_Connect()相同。VixDiskLib_ConnectEx()有三个额外的参数:

  • Boolean设置为TRUE表示只读的访问,通常更快,或者设置为FALSE表示可读、可写。如果采用只读连接,之后调用VixDiskLib_Open()总是只读的,而不管它的openFlags参数如何设置。

  • 通过连接访问的快照的托管对象引用(MoRef)。大多数传输方法(SANHotAddNBDSSL)都需要这个参数,或者用于访问这在运行的虚拟机。你还需要设置connectParams中的vmxSpec属性。如果直接连接到ESX/ESXi主机,就提供ESX/ESXiMoRef,如果连接到vCenter服务器,传递vShpereMoRef,它们是不同的。

  • 可选的传输模式,NULL表示使用默认的传输方式。如果你将SAN作为唯一的传输,但是SAN并不可用,VixDiskLib_ConnectEx()不会失败,但是第一次调用VixDiskLib_Open()函数时则会失败

VixDiskLibConnectionParamscnxParams = {0};

if(appGlobals.isRemote) {

    cnxParams.vmName = vmxSpec;

    cnxParams.serverName = hostname;

    cnxParams.credType = VIXDISKLIB_CRED_UID;

    cnxParams.creds.uid.userName = username;

    cnxParams.cred.uid.password = password;

    cnxParams.port = port;

}

VixErrorvixError = VixDiskLib_ConnectEx(&cnxParams, TRUE, “snapshot-47”, NULL,&connection);

尽管程序在调用VixDiskLib_ConnectEx()时使用NULL作为传输模式参数,以使用默认的传输模式,如果ESX/ESXi主机上的SAN存储可用的话,就会选择SAN作为传输模式。那么如果程序打开一个本地存储上的虚拟磁盘,后续的写操作就会失败。这时,程序就需要指明nbdnbdssl作为传输模式。

参数cnxParams中,vmxSpec指向的在ESX/ESXi主机和在vCenter服务器上的托管对象引用是不一样的:

vmxSpec= “moid=23498”;

vmxSpec= “moid=898273”;

端口(port)参数表示vCenter服务器侦听API请求的那个端口。设置为nullVMDK库将使用默认的通信端口。通常是443(HTTPS)或者902(VIX自动化)。这是数据拷贝的端口,而不是SOAP请求的端口。

获取已选的传输方法

VixDiskLib_GetTransportMode()函数返回针对diskHandle选择的传输方法。

printf(“Selectedtransport method: %s\n”, VixDiskLib_GetTransportMode(diskHandle));

准备和结束访问(Prepare For Access and End Access)

VixDiskLib_PrepareForAccess()函数通知vCenter管理的主机正在打开一个虚拟机磁盘,通常在备份时打开,主机应该推迟可能会干扰虚拟磁盘访问的虚拟机操作。创建虚拟机快照前调用此函数。在内部,这个函数会禁止vSphere中的RelocateVM_Task()方法。

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

连接函数必须指定唯一的虚拟机。打开一个托管磁盘时,需要提供管理磁盘所在ESXi主机的vCenter服务器的有效认证凭据。第二个参数仅用于跟踪目的,且长度不超过50个字符。它可以是虚拟机的名称或者应用程序的名称。如果直接在ESXi主机上调用VixDiskLib_PrepareForAccess(),系统将会抛出错误消息:“VDDK: HostAgent is not avCenter, cannot diskable svMotion”

每一次调用VixDiskLib_PrepareForAcccess()都应该相应的调用一次VixDiskLib_EndAccess()函数。

VixDiskLibEndAccess()函数通知主机虚拟机磁盘已经被关闭,依赖于关闭磁盘的操作现在可以启用了,如vMotion。在关闭所有虚拟磁盘并删除所有虚拟机快照后调用此函数。通常这个函数都在VixDiskLib_PrepareForAccess()之后调用,但是你也可以在奔溃后调用它来做一次清除操作(cleanup)。在内部,这个函数会重新启用vSphereRelocateVM_Task()方法。

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

下面的代码片段中,在备份程序中使用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_Cleanup()就会移除每个虚拟机的额外状态。它的三个参数分别用来指定连接,返回清理的虚拟机个数,以及未清理的虚拟机个数。

intnumCleanedup, numRemaining;

VixErrorvixError = VixDiskLib_Cleanup(&cnxParams, &numCleanedup,&numRemaining);

将程序升级到高级传输

如果要升级应用程序,以支持托管磁盘的高级传输方式,请参考以下步骤:

1找到所有VixDiskLib_Connect()的实例,将它们修改为VixDiskLib_ConnectEx().

vixDiskLib示例程序在使用-mod选项时就会使用VixDiskLib_ConnectEx().

2VixDiskLib_Init()修改为VixDiskLib_InitEx(),并确保只调用一次。

3使用VixDiskLib_PrepareForAccess()禁用虚拟机的迁移(relocation)

4在中间添加如下参数

    a TRUE用于高性能的只读访问,FALSE用于读写访问。

    b 使用快照的moRef

    c NULL使用默认的传输方法(推荐)

5使用VixDiskLib_EndAccess()重新启用虚拟机的迁移。

6在程序结束处找到VixDiskLib_Disconnect(),为了安全起见,直接在后面加上VixDiskLib_Cleanup()

7使用新的可靠且启用了传输模式的VixDiskLib重新编译程序。

在备份或还原由VMware vSphere管理的虚拟磁盘时,高级传输模式非常有用。备份基于快照机制,它提供了在某个时间点的数据视图(View),允许访问静态的父磁盘上数据,而子磁盘保持修改。

vSphere备份算法

经典的备份程序可参考下面的算法:

  • 最好通过vCenter服务器连接ESX/ESXi主机并查找目标虚拟机。

  • 请求ESX/ESXi主机对目标虚拟机创建快照。

  • 使用vSphere接口(PropertyCollector)抓取状态信息(VirutalMachineConfigInfo)以及修改的块信息(queryChangedDiskAreas)

  • 使用高级传输函数和VixDiskLib,访问快照并保存数据。如果启用了块修改跟踪(Changed BlockTracing),快照仅包含增量备份数据。

  • 请求ESX/ESXi主机删除备份快照。

灾难恢复或基于文件的还原算法如下:

最好通过VMware vCenter连接包含目标虚拟机的ESX/ESXi主机。

请求ESX/ESXi主机停止并关闭目标虚拟机。

使用高级传输函数,从备份数据中恢复快照。

对于恢复到某个时间点的灾难恢复,需要将虚拟机恢复到还原的快照。对于基于文件的还原,挂载快照并还原相关的文件。

第七章设计vSphere备份解决方案将会讲述更详细的算法,包括示例代码。

备份还原的例子

VMwarevSphere接口queryChangedDiskArea会返回一个快照和由更改ID标识的早期时间点之间发生修改的磁盘扇区。

queryChangedDiskAreas方法使用4个参数,包括一个快照引用和一个修改ID。它返回一个磁盘扇区的列表,即修改ID和快照之间的时间里发生改变的扇区。如果将修改ID设置为星号(*),该函数会返回所有已分配的磁盘扇区的列表,因此你的备份程序可以跳过稀疏虚拟磁盘的未分配扇区。

假设你在T1时间创建了一个初始备份,之后在T2T3时间点创建了另外的增量备份。(你也可以使用差异备份,它会消耗更多的备份时间和带宽,但是拥有更少的还原时间。)

T1时间的完全备份

1记录虚拟机的配置信息,VirtualMachineConfigInfo

2创建虚拟机快照,命名为snapshot_T1

3获得快照中每个虚拟磁盘的修改IDchangeId_T1(每个磁盘一个)

4备份queryChangedDiskAreas(…“*”)返回的扇区,避免未分配磁盘扇区。

5删除快照snapshot_T1,并和大量的备份数据一起保存修改ID changeId_T1

T2时间的增量备份

1创建虚拟机快照,命名为snapshot_T2

2获得快照中每个虚拟磁盘的修改IDchangeId_T2(每个磁盘一个)

3备份queryChangedDiskAreas(snapshot_T2,… changedId_T1)返回的磁盘扇区。

4删除快照snapshot_T2,并和备份数据一起保存changedId_T2

T3时间的增量备份

1创建虚拟机快照,命名为snapshot_T3。这时你无法再获得T1T2两个时间点之间的修改列表。

2获得快照中每个虚拟磁盘的修改IDchangeId_T3(每个磁盘一个)

3备份queryChangedDiskAreas(snapshot_T3,… changedId_T2)返回的磁盘扇区。如果执行差异备份,则需要调用queryChangedDiskAreas(snapshot_T3,… changedId_T1)

4删除快照snapshot_T3,并和备份数据一起保存changedId_T3

T4时间的灾难恢复

1使用之前保存的VirtualMachineConfigInfo中的配置参数,创建一个没有按照客户机操作系统的新虚拟机。你不需要格式化虚拟磁盘,因为还原的数据包含有格式化的信息。

2T3的备份中还原数据,并记录还原了哪些扇区。

3T2中还原增量备份的数据,跳过已经还原的扇区。如果是差异备份,跳过拷贝T2的数据。

4T1完全备份中还原数据,并跳过已经还原的扇区。从后往前还原的目的就是获取最新的数据,而避免不需要的数据拷贝。

5打开恢复过后的虚拟机。

如果程序使用SAN传输模式打开远程磁盘,它们可以写基准磁盘,但是不能修改快照(重写日志,redo log)。只有在本地主机上的本地磁盘才支持打开并修改快照。

多线程的考虑

在多线程程序中,客户程序应该讲磁盘请求串行化。磁盘句柄不会绑定到单个线程,而是可以跨线程使用。你可以在一个线程中打开磁盘,然后在其他线程中使用,已提供串行化的磁盘访问。相反的,你可以使设计好的打开-关闭线程,正如下面讲述的那样。

多线程和VixDiskLib

VDDK支持到多块磁盘的并发I/O,但是存在一些限制:

VixDiskLib_InitEx()或者VixDiskLib_Init()只能在每个进程的主线程中调用一次。

VixDiskLib_InitEx()后者VixDiskLib_Init()函数中,不要将日志回调函数设置为NULL。这回导致VixDiskLib提供默认的日志函数,而它们是非线程安全的。如果在多线程环境中使用VDDK,必须提供自己的线程安全的日志函数。

在调用VixDiskLib_Open()VixDiskLib_Close()时,VDDK会初始化以及反初始化多个库,在多线程环境中一些库可能不起作用。例如下面的调用将会失败:

线程1VixDiskLib_Open() ……VixDiskLib_Close()

线程2……………………………………….VixDiskLib_Open()…… VixDiskLib_Close()

解决方法是使用一个指定的线程进行所有的打开和关闭操作,而使用其他工作者线程进行读、写操作。下面显示了两个不同磁盘句柄的并行读写。针对同一个磁盘句柄的并行读写是不支持的。

wKioL1OiVD6jRX5XAAB3suPMuCQ831.jpg

库调用的能力

本节说明存在的一些限制。

托管磁盘支持

不支持一些操作:

  • 如果使用VixDiskLib_Connect()打开一个托管磁盘的连接,必须提供有效的vSphere访问认证信息。在ESX/ESXi主机上,VixDiskLib_Open()不能打开一个磁盘链当中的单个连接。

  • 如果要使用VixDiskLib_Create()ESX/ESXi主机上创建一个托管磁盘,首先需要创建一个本地类型的磁盘,然后使用VixDiskLib_Clone()将本地类型虚拟磁盘转换为托管磁盘。

  • VixDiskLib_Defragment()只能针对本地磁盘进行碎片整理。

  • VixDiskLib_Grow()只能扩展本地虚拟磁盘。

  • VixDiskLib_Unlink()只能删除本地磁盘。

  • 直到ESXi5.1HotAdd传输只能用于vSphere企业版或更高版本。

本地磁盘支持

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

  • VixDiskLib_ConnectEx()扩展连接函数。

  • SANHotAdd高级传输。

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