/* $Id: ovfreader.h 49029 2013-10-10 13:17:09Z vboxsync $ */ /** @file * VirtualBox Main - OVF reader declarations. * * Depends only on IPRT, including the RTCString and IPRT XML classes. */ /* * Copyright (C) 2008-2011 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; * you can redistribute it and/or modify it under the terms of the GNU * General Public License (GPL) as published by the Free Software * Foundation, in version 2 as it comes in the "COPYING" file of the * VirtualBox OSE distribution. VirtualBox OSE is distributed in the * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. */ #ifndef ____H_OVFREADER #define ____H_OVFREADER #include "iprt/cpp/xml.h" #include namespace ovf { //////////////////////////////////////////////////////////////////////////////// // // Errors // //////////////////////////////////////////////////////////////////////////////// /** * Thrown by OVFReader for any kind of error that is not an XML error but * still makes the OVF impossible to parse. Based on xml::LogicError so * that one catch() for all xml::LogicError can handle all possible errors. */ class OVFLogicError : public xml::LogicError { public: OVFLogicError(const char *aFormat, ...); }; //////////////////////////////////////////////////////////////////////////////// // // Enumerations // //////////////////////////////////////////////////////////////////////////////// /** * CIM OS values. * * The OVF 1.10 spec refers to some CIM_OperatingSystem.mof doc. Could this be it: * http://cvs.opengroup.org/cgi-bin/cvsweb.cgi/pegasus/Schemas/CIM231/DMTF/System/CIM_OperatingSystem.mof * * @todo r=bird: Why are the values are repeating 'CIMOS'. CIMOSType_T is also * repeating it self, 'Type' and '_T'. Why not call it kCIOMOpSys, * easier to read as well. * Then also apply: s/CIMOSType_CIMOS_/kCIMOpSys_/g */ enum CIMOSType_T { CIMOSType_CIMOS_Unknown = 0, CIMOSType_CIMOS_Other = 1, CIMOSType_CIMOS_MACOS = 2, CIMOSType_CIMOS_ATTUNIX = 3, CIMOSType_CIMOS_DGUX = 4, CIMOSType_CIMOS_DECNT = 5, CIMOSType_CIMOS_Tru64UNIX = 6, CIMOSType_CIMOS_OpenVMS = 7, CIMOSType_CIMOS_HPUX = 8, CIMOSType_CIMOS_AIX = 9, CIMOSType_CIMOS_MVS = 10, CIMOSType_CIMOS_OS400 = 11, CIMOSType_CIMOS_OS2 = 12, CIMOSType_CIMOS_JavaVM = 13, CIMOSType_CIMOS_MSDOS = 14, CIMOSType_CIMOS_WIN3x = 15, CIMOSType_CIMOS_WIN95 = 16, CIMOSType_CIMOS_WIN98 = 17, CIMOSType_CIMOS_WINNT = 18, CIMOSType_CIMOS_WINCE = 19, CIMOSType_CIMOS_NCR3000 = 20, CIMOSType_CIMOS_NetWare = 21, CIMOSType_CIMOS_OSF = 22, CIMOSType_CIMOS_DCOS = 23, CIMOSType_CIMOS_ReliantUNIX = 24, CIMOSType_CIMOS_SCOUnixWare = 25, CIMOSType_CIMOS_SCOOpenServer = 26, CIMOSType_CIMOS_Sequent = 27, CIMOSType_CIMOS_IRIX = 28, CIMOSType_CIMOS_Solaris = 29, CIMOSType_CIMOS_SunOS = 30, CIMOSType_CIMOS_U6000 = 31, CIMOSType_CIMOS_ASERIES = 32, CIMOSType_CIMOS_HPNonStopOS = 33, CIMOSType_CIMOS_HPNonStopOSS = 34, CIMOSType_CIMOS_BS2000 = 35, CIMOSType_CIMOS_LINUX = 36, CIMOSType_CIMOS_Lynx = 37, CIMOSType_CIMOS_XENIX = 38, CIMOSType_CIMOS_VM = 39, CIMOSType_CIMOS_InteractiveUNIX = 40, CIMOSType_CIMOS_BSDUNIX = 41, CIMOSType_CIMOS_FreeBSD = 42, CIMOSType_CIMOS_NetBSD = 43, CIMOSType_CIMOS_GNUHurd = 44, CIMOSType_CIMOS_OS9 = 45, CIMOSType_CIMOS_MACHKernel = 46, CIMOSType_CIMOS_Inferno = 47, CIMOSType_CIMOS_QNX = 48, CIMOSType_CIMOS_EPOC = 49, CIMOSType_CIMOS_IxWorks = 50, CIMOSType_CIMOS_VxWorks = 51, CIMOSType_CIMOS_MiNT = 52, CIMOSType_CIMOS_BeOS = 53, CIMOSType_CIMOS_HPMPE = 54, CIMOSType_CIMOS_NextStep = 55, CIMOSType_CIMOS_PalmPilot = 56, CIMOSType_CIMOS_Rhapsody = 57, CIMOSType_CIMOS_Windows2000 = 58, CIMOSType_CIMOS_Dedicated = 59, CIMOSType_CIMOS_OS390 = 60, CIMOSType_CIMOS_VSE = 61, CIMOSType_CIMOS_TPF = 62, CIMOSType_CIMOS_WindowsMe = 63, CIMOSType_CIMOS_CalderaOpenUNIX = 64, CIMOSType_CIMOS_OpenBSD = 65, CIMOSType_CIMOS_NotApplicable = 66, CIMOSType_CIMOS_WindowsXP = 67, CIMOSType_CIMOS_zOS = 68, CIMOSType_CIMOS_MicrosoftWindowsServer2003 = 69, CIMOSType_CIMOS_MicrosoftWindowsServer2003_64 = 70, CIMOSType_CIMOS_WindowsXP_64 = 71, CIMOSType_CIMOS_WindowsXPEmbedded = 72, CIMOSType_CIMOS_WindowsVista = 73, CIMOSType_CIMOS_WindowsVista_64 = 74, CIMOSType_CIMOS_WindowsEmbeddedforPointofService = 75, CIMOSType_CIMOS_MicrosoftWindowsServer2008 = 76, CIMOSType_CIMOS_MicrosoftWindowsServer2008_64 = 77, CIMOSType_CIMOS_FreeBSD_64 = 78, CIMOSType_CIMOS_RedHatEnterpriseLinux = 79, CIMOSType_CIMOS_RedHatEnterpriseLinux_64 = 80, CIMOSType_CIMOS_Solaris_64 = 81, CIMOSType_CIMOS_SUSE = 82, CIMOSType_CIMOS_SUSE_64 = 83, CIMOSType_CIMOS_SLES = 84, CIMOSType_CIMOS_SLES_64 = 85, CIMOSType_CIMOS_NovellOES = 86, CIMOSType_CIMOS_NovellLinuxDesktop = 87, CIMOSType_CIMOS_SunJavaDesktopSystem = 88, CIMOSType_CIMOS_Mandriva = 89, CIMOSType_CIMOS_Mandriva_64 = 90, CIMOSType_CIMOS_TurboLinux = 91, CIMOSType_CIMOS_TurboLinux_64 = 92, CIMOSType_CIMOS_Ubuntu = 93, CIMOSType_CIMOS_Ubuntu_64 = 94, CIMOSType_CIMOS_Debian = 95, CIMOSType_CIMOS_Debian_64 = 96, CIMOSType_CIMOS_Linux_2_4_x = 97, CIMOSType_CIMOS_Linux_2_4_x_64 = 98, CIMOSType_CIMOS_Linux_2_6_x = 99, CIMOSType_CIMOS_Linux_2_6_x_64 = 100, CIMOSType_CIMOS_Linux_64 = 101, CIMOSType_CIMOS_Other_64 = 102, // types added with CIM 2.25.0 follow: CIMOSType_CIMOS_WindowsServer2008R2 = 103, CIMOSType_CIMOS_VMwareESXi = 104, CIMOSType_CIMOS_Windows7 = 105, CIMOSType_CIMOS_CentOS = 106, CIMOSType_CIMOS_CentOS_64 = 107, CIMOSType_CIMOS_OracleEnterpriseLinux = 108, CIMOSType_CIMOS_OracleEnterpriseLinux_64 = 109, CIMOSType_CIMOS_eComStation = 110 // no new types added with CIM 2.26.0 }; enum OVFVersion_T { OVFVersion_unknown, OVFVersion_0_9, OVFVersion_1_0, OVFVersion_2_0 }; const char* const OVF09_URI_string = "http://www.vmware.com/schema/ovf/1/envelope"; const char* const OVF10_URI_string = "http://schemas.dmtf.org/ovf/envelope/1"; const char* const OVF20_URI_string = "http://schemas.dmtf.org/ovf/envelope/2"; const char* const DTMF_SPECS_URI = "http://schemas.dmtf.org/wbem/cim-html/2/"; //////////////////////////////////////////////////////////////////////////////// // // Envelope data // //////////////////////////////////////////////////////////////////////////////// struct EnvelopeData { OVFVersion_T version;//OVF standard version, it is used internally only by VirtualBox RTCString lang;//language OVFVersion_T getOVFVersion() const { return version; } RTCString getStringOVFVersion() const { if (version == OVFVersion_0_9) return "0.9"; else if (version == OVFVersion_1_0) return "1.0"; else if (version == OVFVersion_2_0) return "2.0"; else return ""; } void setOVFVersion(OVFVersion_T v) { version = v; } }; struct FileReference { RTCString strHref; // value from /References/File/@href (filename) RTCString strDiskId; // value from /References/File/@id () }; typedef std::map FileReferenceMap; //////////////////////////////////////////////////////////////////////////////// // // Hardware definition structs // //////////////////////////////////////////////////////////////////////////////// struct DiskImage { // fields from /DiskSection/Disk RTCString strDiskId; // value from DiskSection/Disk/@diskId int64_t iCapacity; // value from DiskSection/Disk/@capacity; // (maximum size for dynamic images, I guess; we always translate this to bytes) int64_t iPopulatedSize; // optional value from DiskSection/Disk/@populatedSize // (actual used size of disk, always in bytes; can be an estimate of used disk // space, but cannot be larger than iCapacity; -1 if not set) RTCString strFormat; // value from DiskSection/Disk/@format // typically http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized RTCString uuidVbox; // optional; if the file was exported by VirtualBox >= 3.2, // then this has the UUID with which the disk was registered // fields from /References/File; the spec says the file reference from disk can be empty, // so in that case, strFilename will be empty, then a new disk should be created RTCString strHref; // value from /References/File/@href (filename); if empty, then the remaining fields are ignored int64_t iSize; // value from /References/File/@size (optional according to spec; then we set -1 here) int64_t iChunkSize; // value from /References/File/@chunkSize (optional, unsupported) RTCString strCompression; // value from /References/File/@compression (optional, can be "gzip" according to spec) // additional field which has a descriptive size in megabytes derived from the above; this can be used for progress reports uint32_t ulSuggestedSizeMB; }; enum ResourceType_T { ResourceType_Other = 1, ResourceType_ComputerSystem = 2, ResourceType_Processor = 3, ResourceType_Memory = 4, ResourceType_IDEController = 5, ResourceType_ParallelSCSIHBA = 6, ResourceType_FCHBA = 7, ResourceType_iSCSIHBA = 8, ResourceType_IBHCA = 9, ResourceType_EthernetAdapter = 10, ResourceType_OtherNetworkAdapter = 11, ResourceType_IOSlot = 12, ResourceType_IODevice = 13, ResourceType_FloppyDrive = 14, ResourceType_CDDrive = 15, ResourceType_DVDDrive = 16, ResourceType_HardDisk = 17, ResourceType_OtherStorageDevice = 20, ResourceType_USBController = 23, ResourceType_SoundCard = 35 }; enum StorageAccessType_T { StorageAccessType_Unknown = 0, StorageAccessType_Readable = 1, StorageAccessType_Writeable = 2, StorageAccessType_ReadWrite = 3 }; enum ComplianceType_T { ComplianceType_No = 0, ComplianceType_Soft = 1, ComplianceType_Medium = 2, ComplianceType_Strong = 3 }; class VirtualHardwareItem { public: RTCString strDescription; RTCString strCaption; RTCString strElementName; uint32_t ulInstanceID; uint32_t ulParent; ResourceType_T resourceType; RTCString strOtherResourceType; RTCString strResourceSubType; bool fResourceRequired; RTCString strHostResource; // "Abstractly specifies how a device shall connect to a resource on the deployment platform. // Not all devices need a backing." Used with disk items, for which this references a virtual // disk from the Disks section. bool fAutomaticAllocation; bool fAutomaticDeallocation; RTCString strConnection; // "All Ethernet adapters that specify the same abstract network connection name within an OVF // package shall be deployed on the same network. The abstract network connection name shall be // listed in the NetworkSection at the outermost envelope level." We ignore this and only set up // a network adapter depending on the network name. RTCString strAddress; // "Device-specific. For an Ethernet adapter, this specifies the MAC address." int32_t lAddress; // strAddress as an integer, if applicable. RTCString strAddressOnParent; // "For a device, this specifies its location on the controller." RTCString strAllocationUnits; // "Specifies the units of allocation used. For example, “byte * 2^20”." uint64_t ullVirtualQuantity; // "Specifies the quantity of resources presented. For example, “256”." uint64_t ullReservation; // "Specifies the minimum quantity of resources guaranteed to be available." uint64_t ullLimit; // "Specifies the maximum quantity of resources that will be granted." uint64_t ullWeight; // "Specifies a relative priority for this allocation in relation to other allocations." RTCString strConsumerVisibility; RTCString strMappingBehavior; RTCString strPoolID; uint32_t ulBusNumber; // seen with IDE controllers, but not listed in OVF spec uint32_t ulLineNumber; // line number of element in XML source; cached for error messages VirtualHardwareItem() : ulInstanceID(0), fAutomaticAllocation(false), fAutomaticDeallocation(false), ullVirtualQuantity(0), ullReservation(0), ullLimit(0), ullWeight(0), ulBusNumber(0), ulLineNumber(0) { itemName = "Item"; }; void fillItem(const xml::ElementNode *item); void setDefaultFlag() { fDefault = true; } bool isThereDefaultValues() const { return fDefault; } void checkConsistencyAndCompliance() RT_THROW(OVFLogicError) { _checkConsistencyAndCompliance(); } protected: virtual void _checkConsistencyAndCompliance() RT_THROW(OVFLogicError); virtual const RTCString& getItemName() { return _getItemName(); } private: RTCString itemName; bool fDefault;//true means that some fields were absent in the XML and some default values were assigned to. virtual const RTCString& _getItemName() { return itemName; } }; class StorageItem: public VirtualHardwareItem { //see DMTF Schema Documentation http://schemas.dmtf.org/wbem/cim-html/2/ StorageAccessType_T accessType; RTCString strHostExtentName; int16_t hostExtentNameFormat; int16_t hostExtentNameNamespace; int64_t hostExtentStartingAddress; int64_t hostResourceBlockSize; int64_t limit; RTCString strOtherHostExtentNameFormat; RTCString strOtherHostExtentNameNamespace; int64_t reservation; int64_t virtualQuantity; RTCString strVirtualQuantityUnits; int64_t virtualResourceBlockSize; public: StorageItem(): VirtualHardwareItem(), accessType(StorageAccessType_Unknown), hostExtentNameFormat(-1), hostExtentNameNamespace(-1), hostExtentStartingAddress(-1), hostResourceBlockSize(-1), limit(-1), reservation(-1), virtualQuantity(-1), virtualResourceBlockSize(-1) { itemName = "StorageItem"; }; void fillItem(const xml::ElementNode *item); protected: virtual void _checkConsistencyAndCompliance() RT_THROW(OVFLogicError); private: RTCString itemName; virtual const RTCString& _getItemName() { return itemName; } }; class EthernetPortItem: public VirtualHardwareItem { //see DMTF Schema Documentation http://schemas.dmtf.org/wbem/cim-html/2/ uint16_t DefaultPortVID; uint16_t DefaultPriority; uint16_t DesiredVLANEndpointMode; uint32_t GroupID; uint32_t ManagerID; RTCString strNetworkPortProfileID; uint16_t NetworkPortProfileIDType; RTCString strOtherEndpointMode; RTCString strOtherNetworkPortProfileIDTypeInfo; RTCString strPortCorrelationID; uint16_t PortVID; bool Promiscuous; uint64_t ReceiveBandwidthLimit; uint16_t ReceiveBandwidthReservation; bool SourceMACFilteringEnabled; uint32_t VSITypeID; uint8_t VSITypeIDVersion; uint16_t AllowedPriorities[256]; RTCString strAllowedToReceiveMACAddresses; uint16_t AllowedToReceiveVLANs[256]; RTCString strAllowedToTransmitMACAddresses; uint16_t AllowedToTransmitVLANs[256]; public: EthernetPortItem(): VirtualHardwareItem() { itemName = "EthernetPortItem"; }; void fillItem(const xml::ElementNode *item); protected: virtual void _checkConsistencyAndCompliance() RT_THROW(OVFLogicError); private: RTCString itemName; virtual const RTCString& _getItemName() { return itemName; } }; typedef std::map DiskImagesMap; struct VirtualSystem; typedef std::map HardwareItemsMap; struct HardDiskController { uint32_t idController; // instance ID (Item/InstanceId); this gets referenced from VirtualDisk enum ControllerSystemType { IDE, SATA, SCSI }; ControllerSystemType system; // one of IDE, SATA, SCSI RTCString strControllerType; // controller subtype (Item/ResourceSubType); e.g. "LsiLogic"; can be empty (esp. for IDE) // note that we treat LsiLogicSAS as a SCSI controller (system == SCSI) even though VirtualBox // treats it as a fourth class besides IDE, SATA, SCSI int32_t lAddress; // value from OVF "Address" element bool fPrimary; // controller index; this is determined heuristically by the OVF reader and will // be true for the first controller of this type (e.g. IDE primary ctler) or // false for the next (e.g. IDE secondary ctler) HardDiskController() : idController(0), lAddress(0), fPrimary(true) { } }; typedef std::map ControllersMap; struct VirtualDisk { uint32_t idController;// SCSI (or IDE) controller this disk is connected to; // this must match HardDiskController.idController and // points into VirtualSystem.mapControllers uint32_t ulAddressOnParent;// parsed strAddressOnParent of hardware item; will be 0 or 1 for IDE // and possibly higher for disks attached to SCSI controllers (untested) RTCString strDiskId;// if the hard disk has an ovf:/disk/ reference, // this receives the component; points to one of the // references in Appliance::Data.mapDisks bool fEmpty;//true - empty disk, e.g. the component ... is absent. }; typedef std::map VirtualDisksMap; /** * A list of EthernetAdapters is contained in VirtualSystem, representing the * ethernet adapters in the virtual system. */ struct EthernetAdapter { RTCString strAdapterType; // "PCNet32" or "E1000" or whatever; from RTCString strNetworkName; // from }; typedef std::list EthernetAdaptersList; /** * A list of VirtualSystem structs is created by OVFReader::read(). Each refers to * a block in the OVF file. */ struct VirtualSystem { RTCString strName; // copy of VirtualSystem/@id RTCString strDescription; // copy of VirtualSystem/AnnotationSection content, if any CIMOSType_T cimos; RTCString strCimosDesc; // readable description of the cimos type in the case of cimos = 0/1/102 RTCString strTypeVbox; // optional type from @vbox:ostype attribute (VirtualBox 4.0 or higher) RTCString strVirtualSystemType; // generic hardware description; OVF says this can be something like "vmx-4" or "xen"; // VMware Workstation 6.5 is "vmx-07" HardwareItemsMap mapHardwareItems; // map of virtual hardware items, sorted by unique instance ID uint64_t ullMemorySize; // always in bytes, copied from llHardwareItems; default = 0 (unspecified) uint16_t cCPUs; // no. of CPUs, copied from llHardwareItems; default = 1 EthernetAdaptersList llEthernetAdapters; // (one for each VirtualSystem/Item[@ResourceType=10]element) ControllersMap mapControllers; // list of hard disk controllers // (one for each VirtualSystem/Item[@ResourceType=6] element with accumulated data from children) VirtualDisksMap mapVirtualDisks; // (one for each VirtualSystem/Item[@ResourceType=17] element with accumulated data from children) bool fHasFloppyDrive; // true if there's a floppy item in mapHardwareItems bool fHasCdromDrive; // true if there's a CD-ROM item in mapHardwareItems; ISO images are not yet supported by OVFtool bool fHasUsbController; // true if there's a USB controller item in mapHardwareItems RTCString strSoundCardType; // if not empty, then the system wants a soundcard; this then specifies the hardware; // VMware Workstation 6.5 uses "ensoniq1371" for example RTCString strLicenseText; // license info if any; receives contents of VirtualSystem/EulaSection/License RTCString strProduct; // product info if any; receives contents of VirtualSystem/ProductSection/Product RTCString strVendor; // product info if any; receives contents of VirtualSystem/ProductSection/Vendor RTCString strVersion; // product info if any; receives contents of VirtualSystem/ProductSection/Version RTCString strProductUrl; // product info if any; receives contents of VirtualSystem/ProductSection/ProductUrl RTCString strVendorUrl; // product info if any; receives contents of VirtualSystem/ProductSection/VendorUrl const xml::ElementNode // pointer to element under element or NULL if not present *pelmVboxMachine; VirtualSystem() : cimos(CIMOSType_CIMOS_Unknown), ullMemorySize(0), cCPUs(1), fHasFloppyDrive(false), fHasCdromDrive(false), fHasUsbController(false), pelmVboxMachine(NULL) { } }; //////////////////////////////////////////////////////////////////////////////// // // Class OVFReader // //////////////////////////////////////////////////////////////////////////////// /** * OVFReader attempts to open, read in and parse an OVF XML file. This is all done * in the constructor; if there is any kind of error in the file -- filesystem error * from IPRT, XML parsing errors from libxml, or OVF logical errors --, exceptions * are thrown. These are all based on xml::Error. * * Hence, use this class as follows: OVFReader *pReader = NULL; try { pReader = new("/path/to/file.ovf"); } catch (xml::Error &e) { printf("A terrible thing happened: %s", e.what()); } // now go look at pReader->m_llVirtualSystem and what's in there if (pReader) delete pReader; */ class OVFReader { public: OVFReader(const void *pvBuf, size_t cbSize, const RTCString &path); OVFReader(const RTCString &path); // Data fields EnvelopeData m_envelopeData; //data of root element "Envelope" RTCString m_strPath; // file name given to constructor DiskImagesMap m_mapDisks; // map of DiskImage structs, sorted by DiskImage.strDiskId std::list m_llVirtualSystems; // list of virtual systems, created by and valid after read() private: xml::Document m_doc; void parse(); void LoopThruSections(const xml::ElementNode *pReferencesElem, const xml::ElementNode *pCurElem); void HandleDiskSection(const xml::ElementNode *pReferencesElem, const xml::ElementNode *pSectionElem); void HandleNetworkSection(const xml::ElementNode *pSectionElem); void HandleVirtualSystemContent(const xml::ElementNode *pContentElem); }; } // end namespace ovf #endif // !____H_OVFREADER