OVF 与 OVA 之间的区别与转换 – 木子

OVF 与 OVA 之间的区别与转换 – 木子


title: OVF 与 OVA 区别与转换
data: 2019-09-22


在 ESXi 上导出虚拟机的时候,vSphere Web 端只能导出 ovf 格式的虚拟机,无法导出 OVA 格式的,使用 vSphere client 能导出这两种格式。看来一哈官方文档和标准手册,才明白。其实 OVA 是 ovf 的打包文件,导入 ova 格式的时候会自动解包出虚拟机的元数据信息。

OVF

The OVF descriptor contains the metadata about the OVF package. This is an extensible XML document for encoding information, such as product details, virtual hardware requirements, and licensing.

根据 Open Virtualization Format 的标准手册 Open Virtualization Format Specification 的描述,OVF 包含以下文件。确切地来讲 OVF 不是单个文件,而是一个未打包成一个文件的包? 虽然这样讲不太严格😂。打包成一个文件就时 OVA 而已。

  • one OVF descriptor with extension .ovf
  • zero or one OVF manifest with extension .mf
  • zero or one OVF certificate with extension .cert
  • zero or more disk image files
  • zero or more additional resource files, such as ISO images

.mf

An OVF package may have a manifest file containing the SHA digests of individual files in the package.OVF packages authored according to this version of the specification shall use SHA256 digests. The manifest file shall have an extension .mf and the same base name as the .ovf file and be a sibling of the .ovf file. If the manifest file is present, a consumer of the OVF package should verify the digests in the manifest file in the OVF package by computing the actual SHA digests and comparing them with the digests listed in the manifest file. The manifest file shall contain SHA digests for all distinct files referenced in the References element of the OVF descriptor and for no other files.

翻译一哈

一个 OVF 包可能会有一个

file usage need
one OVF descriptor with extension .ovf 虚拟机配置信息 必须
zero or one OVF manifest with extension .mf SHA256 非必须
zero or one OVF certificate with extension .cert 验证证书 非必须
zero or more disk image files 虚拟机磁盘 非必须
zero or more additional resource files, such as ISO images 其他资源 非必须

下面看一个 ovf 文件的示例 ,可以看出 ovf 是 xml 格式的,描述了虚拟机的配置信息、元数据信息

<?xml version='1.0' encoding='UTF-8'?>  <Envelope xmlns="http://schemas.dmtf.org/ovf/envelope/1" xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1" xmlns:vmw="http://www.vmware.com/schema/ovf" xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData" xmlns:vssd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData">    <References>      <File ovf:id="file1" ovf:href="Alpine-240-1.vmdk"/>    </References>    <DiskSection>      <Info>List of the virtual disks</Info>      <Disk ovf:capacityAllocationUnits="byte" ovf:format="http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized" ovf:diskId="vmdisk1" ovf:capacity="2147483648" ovf:fileRef="file1"/>    </DiskSection>    <NetworkSection>      <Info>The list of logical networks</Info>      <Network ovf:name="VM Network">        <Description>The VM Network network</Description>      </Network>    </NetworkSection>    <VirtualSystem ovf:id="Alpine-240">      <Info>A Virtual system</Info>      <Name>Alpine-240</Name>      <OperatingSystemSection ovf:id="101" vmw:osType="otherLinux64Guest">        <Info>The operating system installed</Info>        <Description>其他 Linux (64 位)</Description>      </OperatingSystemSection>      <VirtualHardwareSection>        <Info>Virtual hardware requirements</Info>        <System>          <vssd:ElementName>Virtual Hardware Family</vssd:ElementName>          <vssd:InstanceID>0</vssd:InstanceID>          <vssd:VirtualSystemType>vmx-11</vssd:VirtualSystemType>        </System>        <Item>          <rasd:AllocationUnits>hertz * 10^6</rasd:AllocationUnits>          <rasd:Description>Number of Virtual CPUs</rasd:Description>          <rasd:ElementName>2 virtual CPU(s)</rasd:ElementName>          <rasd:InstanceID>1</rasd:InstanceID>          <rasd:ResourceType>3</rasd:ResourceType>          <rasd:VirtualQuantity>2</rasd:VirtualQuantity>          <vmw:CoresPerSocket ovf:required="false">1</vmw:CoresPerSocket>        </Item>        <Item>          <rasd:AllocationUnits>byte * 2^20</rasd:AllocationUnits>          <rasd:Description>Memory Size</rasd:Description>          <rasd:ElementName>2048MB of memory</rasd:ElementName>          <rasd:InstanceID>2</rasd:InstanceID>          <rasd:ResourceType>4</rasd:ResourceType>          <rasd:VirtualQuantity>2048</rasd:VirtualQuantity>        </Item>        <Item>          <rasd:Address>0</rasd:Address>          <rasd:Description>SCSI Controller</rasd:Description>          <rasd:ElementName>SCSI Controller 1</rasd:ElementName>          <rasd:InstanceID>3</rasd:InstanceID>          <rasd:ResourceSubType>lsilogic</rasd:ResourceSubType>          <rasd:ResourceType>6</rasd:ResourceType>          <vmw:Config ovf:required="false" vmw:key="slotInfo.pciSlotNumber" vmw:value="16"/>        </Item>        <Item>          <rasd:AddressOnParent>0</rasd:AddressOnParent>          <rasd:ElementName>Hard Disk 1</rasd:ElementName>          <rasd:HostResource>ovf:/disk/vmdisk1</rasd:HostResource>          <rasd:InstanceID>4</rasd:InstanceID>          <rasd:Parent>3</rasd:Parent>          <rasd:ResourceType>17</rasd:ResourceType>        </Item>        <Item>          <rasd:AddressOnParent>0</rasd:AddressOnParent>          <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>          <rasd:Connection>VM Network</rasd:Connection>          <rasd:ElementName>Network adapter 1</rasd:ElementName>          <rasd:InstanceID>5</rasd:InstanceID>          <rasd:ResourceSubType>E1000</rasd:ResourceSubType>          <rasd:ResourceType>10</rasd:ResourceType>          <vmw:Config ovf:required="false" vmw:key="connectable.allowGuestControl" vmw:value="true"/>          <vmw:Config ovf:required="false" vmw:key="slotInfo.pciSlotNumber" vmw:value="32"/>          <vmw:Config ovf:required="false" vmw:key="wakeOnLanEnabled" vmw:value="true"/>        </Item>        <Item ovf:required="false">          <rasd:ElementName>Video card</rasd:ElementName>          <rasd:InstanceID>6</rasd:InstanceID>          <rasd:ResourceType>24</rasd:ResourceType>          <vmw:Config ovf:required="false" vmw:key="videoRamSizeInKB" vmw:value="4096"/>          <vmw:Config ovf:required="false" vmw:key="useAutoDetect" vmw:value="false"/>          <vmw:Config ovf:required="false" vmw:key="graphicsMemorySizeInKB" vmw:value="262144"/>          <vmw:Config ovf:required="false" vmw:key="numDisplays" vmw:value="1"/>          <vmw:Config ovf:required="false" vmw:key="enable3DSupport" vmw:value="false"/>          <vmw:Config ovf:required="false" vmw:key="use3dRenderer" vmw:value="automatic"/>        </Item>        <vmw:Config ovf:required="false" vmw:key="cpuHotAddEnabled" vmw:value="false"/>        <vmw:Config ovf:required="false" vmw:key="nestedHVEnabled" vmw:value="false"/>        <vmw:Config ovf:required="false" vmw:key="virtualSMCPresent" vmw:value="false"/>        <vmw:Config ovf:required="false" vmw:key="cpuHotRemoveEnabled" vmw:value="false"/>        <vmw:Config ovf:required="false" vmw:key="memoryHotAddEnabled" vmw:value="false"/>        <vmw:Config ovf:required="false" vmw:key="firmware" vmw:value="bios"/>        <vmw:Config ovf:required="false" vmw:key="virtualICH7MPresent" vmw:value="false"/>        <vmw:ExtraConfig ovf:required="false" vmw:key="nvram" vmw:value="Alpine-240.nvram"/>        <vmw:ExtraConfig ovf:required="false" vmw:key="virtualHW.productCompatibility" vmw:value="hosted"/>      </VirtualHardwareSection>    </VirtualSystem>  </Envelope>          

下面是 mf 文件信息

SHA256(Alpine-240.ovf)= 3d5b06e6741da7919e33775fb2c1b3e77968b4d7b020f4055a8a401f1127be29  SHA256(Alpine-240-1.vmdk)= b41596be4a846877cf82c4fd221763ba1cb4384f178454dc064f60f2ccdfd50e

在导入 ovf 格式的虚拟机文件时,会解析这个ovf 文件,通过这个文件里描述的设备信息自动创建新的虚拟机。

OVA

根据官方的描述

An OVF package can be stored as either a single compressed file (.ova) or a set of files

An OVF package may be stored as a compressed OVF package or as a set of files in a directory structure. A compressed OVF package is stored as single file. The file extension is .ova (open virtual appliance or application).

In addition, the entries shall be in one of the following orders inside the OVF package:

  • 1) OVF descriptor
  • 2) The remaining files shall be in the same order as listed in the References section

or

  • 1) OVF descriptor
  • 2) OVF manifest
  • 3) OVF certificate

or

  • 1) OVF descriptor
  • 2) The intermediate files shall be in the same order as listed in the References section
  • 3) OVF manifest
  • 4) OVF certificate

需要注意的是,OVA 单个文件里打包了 OVF 所有的文件,文件是有顺序的,第一个一定要是 OVF 描述文件,即导出虚拟机时的那个 .ovf 后缀的文件

转换

OVA 转 OVF 很简单,使用 tar 解包就行

比如任意解包一个 OVA 文件后会出来 ovf vmdk mf 这三个文件,而且解包出来的顺序也是和 OVA 标准定义的那样,第一个必须未 .ovf 文件。这里我讲的是解包而不是解压,是因为 OVA 和 OVF 里包含的文件,最大的就是磁盘文件,而磁盘文件在导出的时候虚拟机已经进行了压缩,你可以使用 df 命令看看磁盘占用的空间,以及导出的磁盘占用的空间,你就会发现导出的磁盘文件大小远小于系统占用的空间。

tar -xvf Ubuntu1804.ova  Ubuntu1804.ovf  Ubuntu1804-disk1.vmdk  Ubuntu1804.mf

OVF 转 OVA

按照 OVA 标准的格式,按顺序打包 OVF 包里的文件就行,注意 .ovf 文件一定要在第一个

tar -cf OP.ova Ubuntu1804.ovf Ubuntu1804-disk1.vmdk Ubuntu1804.mf

OVA = tar OVF ?

我们把 OVA 解包出来,再打包回去,两者文件是否一样呢?

看一下两者的 sha256sum 信息就可以了,结果证明是不一样的

e6b0f08dc80ef6509cd547b87d8fe9373069d6758b86f3cbb43e804a3c9b7e7d  Ubuntu1804.ova  d56d3fa5f3a57f7210726303ef32fd96d98cc7522af4f4fdbac2be7f23a5cadb  OP.ova

两者导入虚拟机后没有任何差别,都可以导进去,都能开机和使用,但新生成的 OVA 文件元数据信息是不一样的。

使用 less 命令看一下两者的文件头

原来的 OVA 文件头

新生成的 OVA 文件头

第一眼看到 644 755 777 就发现,这一定是文件的权限,其中还有文件所属用户,原来的是 VMware 用户,而新生成的是我本地的用户。

刨根到底,去看一哈 tar 包的定义Basic Tar Format tar 包就是由一个个文件顺序排列而成,每个文件由两部分组成:文件头和文件内容。就像下面这样

|文件头|文件内容|--|文件头|文件内容|--|文件头|文件内容|--|文件头|文件内容|

我们从 OVA 解包出来后,文件头的内容就会更改为我们本机的内容,而不再是原有的文件所主。

文件头的定义在

        struct posix_header  {                           char name[100];          文件名    char mode[8];            用户权限    char uid[8];             user id    char gid[8];             group id    char size[12];           文件大小    char mtime[12];          修改时间    char chksum[8];          校验值    char typeflag;           文件类型标志    char linkname[100];      符号链接指向    char magic[6];               char version[2];          char uname[32];          user name    char gname[32];          group name    char devmajor[8];        设备文件 major    char devminor[8];        设备文件 minor    char prefix[155];                                 };    #define TMAGIC   "ustar"          #define TMAGLEN  6  #define TVERSION "00"             #define TVERSLEN 2      #define REGTYPE  '0'              #define AREGTYPE '\0'             #define LNKTYPE  '1'              #define SYMTYPE  '2'              #define CHRTYPE  '3'              #define BLKTYPE  '4'              #define DIRTYPE  '5'              #define FIFOTYPE '6'              #define CONTTYPE '7'                #define XHDTYPE  'x'              #define XGLTYPE  'g'                  #define TSUID    04000            #define TSGID    02000            #define TSVTX    01000                                              #define TUREAD   00400            #define TUWRITE  00200            #define TUEXEC   00100            #define TGREAD   00040            #define TGWRITE  00020            #define TGEXEC   00010            #define TOREAD   00004            #define TOWRITE  00002            #define TOEXEC   00001                              struct sparse  {                                  char offset[12];                  char numbytes[12];                                                };        #define SPARSES_IN_EXTRA_HEADER  16  #define SPARSES_IN_OLDGNU_HEADER 4  #define SPARSES_IN_SPARSE_HEADER 21        struct sparse_header  {                                  struct sparse sp[SPARSES_IN_SPARSE_HEADER];                                      char isextended;                                                  };        struct oldgnu_header  {                                  char unused_pad1[345];            char atime[12];                   char ctime[12];                   char offset[12];                  char longnames[4];                char unused_pad2;                 struct sparse sp[SPARSES_IN_OLDGNU_HEADER];                                      char isextended;                  char realsize[12];                                                };      #define OLDGNU_MAGIC "ustar  "            #define GNUTYPE_DUMPDIR 'D'      #define GNUTYPE_LONGLINK 'K'      #define GNUTYPE_LONGNAME 'L'      #define GNUTYPE_MULTIVOL 'M'      #define GNUTYPE_SPARSE 'S'      #define GNUTYPE_VOLHDR 'V'      #define SOLARIS_XHDTYPE 'X'        struct star_header  {                                  char name[100];                   char mode[8];                     char uid[8];                      char gid[8];                      char size[12];                    char mtime[12];                   char chksum[8];                   char typeflag;                    char linkname[100];               char magic[6];                    char version[2];                  char uname[32];                   char gname[32];                   char devmajor[8];                 char devminor[8];                 char prefix[131];                 char atime[12];                   char ctime[12];                                                   };    #define SPARSES_IN_STAR_HEADER      4  #define SPARSES_IN_STAR_EXT_HEADER  21    struct star_in_header  {    char fill[345];           char prefix[1];           char fill2;               char fill3[8];            char isextended;          struct sparse sp[SPARSES_IN_STAR_HEADER];     char realsize[12];        char offset[12];          char atime[12];           char ctime[12];           char mfill[8];            char xmagic[4];         };    struct star_ext_header  {    struct sparse sp[SPARSES_IN_STAR_EXT_HEADER];    char isextended;  };  

收获

通过阅读官方标准手册对 OVF 和 OVA 有两个更深刻的了解,对 tar 包的格式和 Unix 文件头初次了解。这么好的资料准备有空翻译几个关键的章节来水一篇博客😂