VirtualBox

source: vbox/trunk/src/VBox/Main/xml/ovfreader.cpp@ 99569

Last change on this file since 99569 was 99523, checked in by vboxsync, 20 months ago

bugref:10314. Reverted back r154561 partially. RAM unit is MB.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 49.9 KB
Line 
1/* $Id: ovfreader.cpp 99523 2023-04-24 14:44:16Z vboxsync $ */
2/** @file
3 * OVF reader declarations.
4 *
5 * Depends only on IPRT, including the RTCString and IPRT XML classes.
6 */
7
8/*
9 * Copyright (C) 2008-2023 Oracle and/or its affiliates.
10 *
11 * This file is part of VirtualBox base platform packages, as
12 * available from https://www.virtualbox.org.
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation, in version 3 of the
17 * License.
18 *
19 * This program is distributed in the hope that it will be useful, but
20 * WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, see <https://www.gnu.org/licenses>.
26 *
27 * SPDX-License-Identifier: GPL-3.0-only
28 */
29
30#define LOG_GROUP LOG_GROUP_MAIN_APPLIANCE
31#include "ovfreader.h"
32#include <VBox/log.h>
33#include <vector>
34
35using namespace std;
36using namespace ovf;
37
38
39
40////////////////////////////////////////////////////////////////////////////////
41//
42// OVF reader implementation
43//
44////////////////////////////////////////////////////////////////////////////////
45
46/**
47 * Default Constructor.
48 * Should be used if you don't have an OVF file, but want to fill the data
49 * m_mapDisks, m_llVirtualSystems manually
50 */
51OVFReader::OVFReader()
52{
53}
54
55/**
56 * Constructor. This parses the given XML file out of the memory. Throws lots of exceptions
57 * on XML or OVF invalidity.
58 * @param pvBuf the memory buffer to parse
59 * @param cbSize the size of the memory buffer
60 * @param path path to a filename for error messages.
61 */
62OVFReader::OVFReader(const void *pvBuf, size_t cbSize, const RTCString &path)
63 : m_strPath(path)
64{
65 xml::XmlMemParser parser;
66 parser.read(pvBuf, cbSize,
67 m_strPath,
68 m_doc);
69 /* Start the parsing */
70 parse();
71}
72
73/**
74 * Constructor. This opens the given XML file and parses it. Throws lots of exceptions
75 * on XML or OVF invalidity.
76 * @param path
77 */
78OVFReader::OVFReader(const RTCString &path)
79 : m_strPath(path)
80{
81 xml::XmlFileParser parser;
82 parser.read(m_strPath,
83 m_doc);
84 /* Start the parsing */
85 parse();
86}
87
88void OVFReader::parse()
89{
90 const xml::ElementNode *pRootElem = m_doc.getRootElement();
91 const xml::AttributeNode *pTypeAttr;
92 const char *pcszTypeAttr = "";
93 RTCString pcszNamespaceURI;
94
95 if (!pRootElem || strcmp(pRootElem->getName(), "Envelope"))
96 throw OVFLogicError(N_("Root element in OVF file must be 'Envelope'."));
97
98 pcszNamespaceURI = pRootElem->getNamespaceURI();
99 if (pcszNamespaceURI.isEmpty())
100 {
101 throw OVFLogicError(N_("Error reading namespace URI in 'Envelope' element, line %d"), pRootElem->getLineNumber());
102 }
103
104 if (strncmp(ovf::OVF20_URI_string, pcszNamespaceURI.c_str(), pcszNamespaceURI.length()) == 0)
105 {
106 m_envelopeData.setOVFVersion(ovf::OVFVersion_2_0);
107 }
108 else if (strncmp(OVF10_URI_string, pcszNamespaceURI.c_str(), pcszNamespaceURI.length()) == 0)
109 {
110 m_envelopeData.setOVFVersion(ovf::OVFVersion_1_0);
111 }
112 else
113 {
114 m_envelopeData.setOVFVersion(ovf::OVFVersion_0_9);
115 }
116
117 if ((pTypeAttr = pRootElem->findAttribute("lang", "xml")))
118 {
119 pcszTypeAttr = pTypeAttr->getValueN(RT_XML_ATTR_TINY);
120 m_envelopeData.lang = pcszTypeAttr;
121 }
122
123 // OVF has the following rough layout:
124 /*
125 -- <References> .... files referenced from other parts of the file, such as VMDK images
126 -- Metadata, comprised of several section commands
127 -- virtual machines, either a single <VirtualSystem>, or a <VirtualSystemCollection>
128 -- optionally <Strings> for localization
129 */
130
131 // get all "File" child elements of "References" section so we can look up files easily;
132 // first find the "References" sections so we can look up files
133 xml::ElementNodesList listFileElements; // receives all /Envelope/References/File nodes
134 const xml::ElementNode *pReferencesElem;
135 if ((pReferencesElem = pRootElem->findChildElement("References")))
136 pReferencesElem->getChildElements(listFileElements, "File");
137
138 // now go though the sections
139 LoopThruSections(pReferencesElem, pRootElem);
140}
141
142/**
143 * Private helper method that goes thru the elements of the given "current" element in the OVF XML
144 * and handles the contained child elements (which can be "Section" or "Content" elements).
145 *
146 * @param pReferencesElem "References" element from OVF, for looking up file specifications;
147 * can be NULL if no such element is present.
148 * @param pCurElem Element whose children are to be analyzed here.
149 * @return
150 */
151void OVFReader::LoopThruSections(const xml::ElementNode *pReferencesElem,
152 const xml::ElementNode *pCurElem)
153{
154 xml::NodesLoop loopChildren(*pCurElem);
155 const xml::ElementNode *pElem;
156 while ((pElem = loopChildren.forAllNodes()))
157 {
158 const char *pcszElemName = pElem->getName();
159 const xml::AttributeNode *pTypeAttr = pElem->findAttribute("type");
160 const char *pcszTypeAttr = pTypeAttr ? pTypeAttr->getValueN(RT_XML_ATTR_TINY) : "";
161
162 if ( !strcmp(pcszElemName, "DiskSection")
163 || ( !strcmp(pcszElemName, "Section")
164 && !strcmp(pcszTypeAttr, "ovf:DiskSection_Type")
165 )
166 )
167 {
168 HandleDiskSection(pReferencesElem, pElem);
169 }
170 else if ( !strcmp(pcszElemName, "NetworkSection")
171 || ( !strcmp(pcszElemName, "Section")
172 && !strcmp(pcszTypeAttr, "ovf:NetworkSection_Type")
173 )
174 )
175 {
176 HandleNetworkSection(pElem);
177 }
178 else if ( !strcmp(pcszElemName, "DeploymentOptionSection"))
179 {
180 /// @todo
181 }
182 else if ( !strcmp(pcszElemName, "Info"))
183 {
184 // child of VirtualSystemCollection -- TODO
185 }
186 else if ( !strcmp(pcszElemName, "ResourceAllocationSection"))
187 {
188 // child of VirtualSystemCollection -- TODO
189 }
190 else if ( !strcmp(pcszElemName, "StartupSection"))
191 {
192 // child of VirtualSystemCollection -- TODO
193 }
194 else if ( !strcmp(pcszElemName, "VirtualSystem")
195 || ( !strcmp(pcszElemName, "Content")
196 && !strcmp(pcszTypeAttr, "ovf:VirtualSystem_Type")
197 )
198 )
199 {
200 HandleVirtualSystemContent(pElem);
201 }
202 else if ( !strcmp(pcszElemName, "VirtualSystemCollection")
203 || ( !strcmp(pcszElemName, "Content")
204 && !strcmp(pcszTypeAttr, "ovf:VirtualSystemCollection_Type")
205 )
206 )
207 {
208 /// @todo ResourceAllocationSection
209
210 // recurse for this, since it has VirtualSystem elements as children
211 LoopThruSections(pReferencesElem, pElem);
212 }
213 }
214}
215
216/**
217 * Private helper method that handles disk sections in the OVF XML.
218 *
219 * Gets called indirectly from IAppliance::read().
220 *
221 * @param pReferencesElem "References" element from OVF, for looking up file
222 * specifications; can be NULL if no such element is
223 * present.
224 * @param pSectionElem Section element for which this helper is getting called.
225 */
226void OVFReader::HandleDiskSection(const xml::ElementNode *pReferencesElem,
227 const xml::ElementNode *pSectionElem)
228{
229 // contains "Disk" child elements
230 xml::NodesLoop loopDisks(*pSectionElem, "Disk");
231 const xml::ElementNode *pelmDisk;
232 while ((pelmDisk = loopDisks.forAllNodes()))
233 {
234 DiskImage d;
235 const char *pcszBad = NULL;
236 const char *pcszDiskId;
237 const char *pcszFormat;
238 if (!pelmDisk->getAttributeValueN("diskId", pcszDiskId, RT_XML_ATTR_TINY))
239 pcszBad = "diskId";
240 else if (!pelmDisk->getAttributeValueN("format", pcszFormat, RT_XML_ATTR_SMALL))
241 pcszBad = "format";
242 else if (!pelmDisk->getAttributeValue("capacity", d.iCapacity))
243 pcszBad = "capacity";
244 else
245 {
246 d.strDiskId = pcszDiskId;
247 d.strFormat = pcszFormat;
248
249 if (!pelmDisk->getAttributeValue("populatedSize", d.iPopulatedSize))
250 // optional
251 d.iPopulatedSize = -1;
252
253 // optional vbox:uuid attribute (if OVF was exported by VirtualBox != 3.2)
254 pelmDisk->getAttributeValueN("uuid", d.uuidVBox, RT_XML_ATTR_TINY, "vbox");
255
256 const char *pcszFileRef;
257 if (pelmDisk->getAttributeValueN("fileRef", pcszFileRef, RT_XML_ATTR_SMALL)) // optional
258 {
259 // look up corresponding /References/File nodes (list built above)
260 const xml::ElementNode *pFileElem;
261 if ( pReferencesElem
262 && (pFileElem = pReferencesElem->findChildElementFromId(pcszFileRef)) != NULL
263 )
264 {
265
266 // copy remaining values from file node then
267 const char *pcszBadInFile = NULL;
268 const char *pcszHref;
269 if (!pFileElem->getAttributeValueN("href", pcszHref, RT_XML_ATTR_SMALL))
270 pcszBadInFile = "href";
271 else if (!pFileElem->getAttributeValue("size", d.iSize))
272 d.iSize = -1; // optional
273
274 d.strHref = pcszHref;
275
276 // if (!(pFileElem->getAttributeValue("size", d.iChunkSize))) TODO
277 d.iChunkSize = -1; // optional
278 const char *pcszCompression;
279 if (pFileElem->getAttributeValueN("compression", pcszCompression, RT_XML_ATTR_TINY))
280 d.strCompression = pcszCompression;
281
282 if (pcszBadInFile)
283 throw OVFLogicError(N_("Error reading \"%s\": missing or invalid attribute '%s' in 'File' element, line %d"),
284 m_strPath.c_str(),
285 pcszBadInFile,
286 pFileElem->getLineNumber());
287 }
288 else
289 throw OVFLogicError(N_("Error reading \"%s\": cannot find References/File element for ID \"%s\" referenced by 'Disk' element, line %d"),
290 m_strPath.c_str(),
291 pcszFileRef,
292 pelmDisk->getLineNumber());
293 }
294 }
295
296 if (pcszBad)
297 throw OVFLogicError(N_("Error reading \"%s\": missing or invalid attribute '%s' in 'DiskSection' element, line %d"),
298 m_strPath.c_str(),
299 pcszBad,
300 pelmDisk->getLineNumber());
301
302 // suggest a size in megabytes to help callers with progress reports
303 d.ulSuggestedSizeMB = 0;
304 if (d.iCapacity != -1)
305 d.ulSuggestedSizeMB = (uint32_t)(d.iCapacity / _1M);
306 else if (d.iPopulatedSize != -1)
307 d.ulSuggestedSizeMB = (uint32_t)(d.iPopulatedSize / _1M);
308 else if (d.iSize != -1)
309 d.ulSuggestedSizeMB = (uint32_t)(d.iSize / _1M);
310 if (d.ulSuggestedSizeMB == 0)
311 d.ulSuggestedSizeMB = 10000; // assume 10 GB, this is for the progress bar only anyway
312
313 m_mapDisks[d.strDiskId] = d;
314 }
315}
316
317/**
318 * Private helper method that handles network sections in the OVF XML.
319 * Gets called indirectly from IAppliance::read().
320 *
321 * @return
322 */
323void OVFReader::HandleNetworkSection(const xml::ElementNode * /* pSectionElem */)
324{
325 // we ignore network sections for now
326
327// xml::NodesLoop loopNetworks(*pSectionElem, "Network");
328// const xml::Node *pelmNetwork;
329// while ((pelmNetwork = loopNetworks.forAllNodes()))
330// {
331// Network n;
332// if (!(pelmNetwork->getAttributeValue("name", n.strNetworkName)))
333// return setError(VBOX_E_FILE_ERROR,
334// tr("Error reading \"%s\": missing 'name' attribute in 'Network', line %d"),
335// pcszPath,
336// pelmNetwork->getLineNumber());
337//
338// m->mapNetworks[n.strNetworkName] = n;
339// }
340}
341
342/**
343 * Private helper method that handles a "VirtualSystem" element in the OVF XML.
344 * Gets called indirectly from IAppliance::read().
345 *
346 * @param pelmVirtualSystem
347 * @return
348 */
349void OVFReader::HandleVirtualSystemContent(const xml::ElementNode *pelmVirtualSystem)
350{
351 /* Create a new virtual system and work directly on the list copy. */
352 m_llVirtualSystems.push_back(VirtualSystem());
353 VirtualSystem &vsys = m_llVirtualSystems.back();
354
355 // peek under the <VirtualSystem> node whether we have a <vbox:Machine> node;
356 // that case case, the caller can completely ignore the OVF but only load the VBox machine XML
357 vsys.pelmVBoxMachine = pelmVirtualSystem->findChildElementNS("vbox", "Machine");
358
359 // now look for real OVF
360 const xml::AttributeNode *pIdAttr = pelmVirtualSystem->findAttribute("id");
361 if (pIdAttr)
362 vsys.strName = pIdAttr->getValueN(RT_XML_ATTR_SMALL);
363
364 xml::NodesLoop loop(*pelmVirtualSystem); // all child elements
365 const xml::ElementNode *pelmThis;
366 while ((pelmThis = loop.forAllNodes()))
367 {
368 const char *pcszElemName = pelmThis->getName();
369 const char *pcszTypeAttr = "";
370 if (!strcmp(pcszElemName, "Section")) // OVF 0.9 used "Section" element always with a varying "type" attribute
371 {
372 const xml::AttributeNode *pTypeAttr = pelmThis->findAttribute("type");
373 if (pTypeAttr)
374 pcszTypeAttr = pTypeAttr->getValueN(RT_XML_ATTR_TINY);
375 else
376 throw OVFLogicError(N_("Error reading \"%s\": element 'Section' has no 'type' attribute, line %d"),
377 m_strPath.c_str(),
378 pelmThis->getLineNumber());
379 }
380
381 if ( !strcmp(pcszElemName, "EulaSection")
382 || !strcmp(pcszTypeAttr, "ovf:EulaSection_Type")
383 )
384 {
385 /* <EulaSection>
386 <Info ovf:msgid="6">License agreement for the Virtual System.</Info>
387 <License ovf:msgid="1">License terms can go in here.</License>
388 </EulaSection> */
389
390 const xml::ElementNode *pelmLicense;
391 if ((pelmLicense = pelmThis->findChildElement("License")))
392 vsys.strLicenseText = pelmLicense->getValueN(RT_XML_CONTENT_LARGE);
393 }
394 if ( !strcmp(pcszElemName, "ProductSection")
395 || !strcmp(pcszTypeAttr, "ovf:ProductSection_Type")
396 )
397 {
398 /* <Section ovf:required="false" xsi:type="ovf:ProductSection_Type">
399 <Info>Meta-information about the installed software</Info>
400 <Product>VAtest</Product>
401 <Vendor>SUN Microsystems</Vendor>
402 <Version>10.0</Version>
403 <ProductUrl>http://blogs.sun.com/VirtualGuru</ProductUrl>
404 <VendorUrl>http://www.sun.com</VendorUrl>
405 </Section> */
406 const xml::ElementNode *pelmProduct;
407 if ((pelmProduct = pelmThis->findChildElement("Product")))
408 vsys.strProduct = pelmProduct->getValueN(RT_XML_CONTENT_SMALL);
409 const xml::ElementNode *pelmVendor;
410 if ((pelmVendor = pelmThis->findChildElement("Vendor")))
411 vsys.strVendor = pelmVendor->getValueN(RT_XML_CONTENT_SMALL);
412 const xml::ElementNode *pelmVersion;
413 if ((pelmVersion = pelmThis->findChildElement("Version")))
414 vsys.strVersion = pelmVersion->getValueN(RT_XML_CONTENT_SMALL);
415 const xml::ElementNode *pelmProductUrl;
416 if ((pelmProductUrl = pelmThis->findChildElement("ProductUrl")))
417 vsys.strProductUrl = pelmProductUrl->getValueN(RT_XML_CONTENT_SMALL);
418 const xml::ElementNode *pelmVendorUrl;
419 if ((pelmVendorUrl = pelmThis->findChildElement("VendorUrl")))
420 vsys.strVendorUrl = pelmVendorUrl->getValueN(RT_XML_CONTENT_SMALL);
421 }
422 else if ( !strcmp(pcszElemName, "VirtualHardwareSection")
423 || !strcmp(pcszTypeAttr, "ovf:VirtualHardwareSection_Type")
424 )
425 {
426 const xml::ElementNode *pelmSystem, *pelmVirtualSystemType;
427 if ((pelmSystem = pelmThis->findChildElement("System")))
428 {
429 /* <System>
430 <vssd:Description>Description of the virtual hardware section.</vssd:Description>
431 <vssd:ElementName>vmware</vssd:ElementName>
432 <vssd:InstanceID>1</vssd:InstanceID>
433 <vssd:VirtualSystemIdentifier>MyLampService</vssd:VirtualSystemIdentifier>
434 <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType>
435 </System>*/
436 if ((pelmVirtualSystemType = pelmSystem->findChildElement("VirtualSystemType")))
437 vsys.strVirtualSystemType = pelmVirtualSystemType->getValueN(RT_XML_CONTENT_SMALL);
438 }
439
440 /* Parse the items into the hardware item vector. */
441 {
442 std::map<RTCString, const VirtualHardwareItem *> mapHardwareItems;
443 xml::NodesLoop childrenIterator(*pelmThis);
444 const xml::ElementNode *pelmItem;
445 while ((pelmItem = childrenIterator.forAllNodes()) != NULL)
446 {
447 /* Parse according to type. */
448 VirtualHardwareItem *pItem;
449 const char *pszName = pelmItem->getName();
450 if (RTStrCmp(pszName, "Item") == 0)
451 pItem = new VirtualHardwareItem();
452 else if (RTStrCmp(pszName, "StorageItem") == 0)
453 pItem = new StorageItem();
454 else if (RTStrCmp(pszName, "EthernetPortItem") == 0)
455 pItem = new EthernetPortItem();
456 else
457 continue;
458 vsys.vecHardwareItems.push_back(pItem);
459 pItem->m_iLineNumber = pelmItem->getLineNumber();
460 pItem->fillItem(pelmItem);
461
462 /* validate */
463 try
464 {
465 pItem->checkConsistencyAndCompliance();
466 }
467 catch (OVFLogicError &e)
468 {
469 throw OVFLogicError(N_("Error reading \"%s\": \"%s\""), m_strPath.c_str(), e.what());
470 }
471
472 /* Add to mapping vector (for parent ID lookups) if it has a valid instance ID. */
473 if (!pItem->strInstanceID.isEmpty())
474 {
475 std::map<RTCString, const VirtualHardwareItem *>::const_iterator itDup;
476 itDup = mapHardwareItems.find(pItem->strInstanceID);
477 if (itDup == mapHardwareItems.end())
478 mapHardwareItems[pItem->strInstanceID] = pItem;
479 else
480#if 1
481 LogRel(("OVFREADER: Warning reading \"%s\": Duplicate InstanceID \"%s\" on line %d, previous at %d!\n",
482 m_strPath.c_str(), pItem->strInstanceID.c_str(), pItem->m_iLineNumber, itDup->second->m_iLineNumber));
483#else
484 throw OVFLogicError(N_("Error reading \"%s\": Duplicate InstanceID \"%s\" on line %d, previous at %d"),
485 m_strPath.c_str(), pItem->strInstanceID.c_str(),
486 pItem->m_iLineNumber, itDup->second->m_iLineNumber);
487#endif
488 }
489 }
490 }
491
492 HardDiskController *pPrimaryIDEController = NULL;// will be set once found
493
494 // now go thru all hardware items and handle them according to their type;
495 // in this first loop we handle all items _except_ hard disk images,
496 // which we'll handle in a second loop below
497 HardwareItemVector::const_iterator itH;
498 for (itH = vsys.vecHardwareItems.begin(); itH != vsys.vecHardwareItems.end(); ++itH)
499 {
500 const VirtualHardwareItem &i = **itH;
501
502 // do some analysis
503 switch (i.resourceType)
504 {
505 case ResourceType_Processor: // 3
506 /* <rasd:Caption>1 virtual CPU</rasd:Caption>
507 <rasd:Description>Number of virtual CPUs</rasd:Description>
508 <rasd:ElementName>virtual CPU</rasd:ElementName>
509 <rasd:InstanceID>1</rasd:InstanceID>
510 <rasd:ResourceType>3</rasd:ResourceType>
511 <rasd:VirtualQuantity>1</rasd:VirtualQuantity>*/
512 if (i.ullVirtualQuantity < UINT16_MAX)
513 vsys.cCPUs = (uint16_t)i.ullVirtualQuantity;
514 else
515 throw OVFLogicError(N_("Error reading \"%s\": CPU count %RI64 is larger than %d, line %d"),
516 m_strPath.c_str(),
517 i.ullVirtualQuantity,
518 UINT16_MAX,
519 i.m_iLineNumber);
520 break;
521
522 case ResourceType_Memory: // 4
523 if ( i.strAllocationUnits == "MegaBytes" // found in OVF created by OVF toolkit
524 || i.strAllocationUnits == "MB" // found in MS docs
525 || i.strAllocationUnits == "byte * 2^20" // suggested by OVF spec DSP0243 page 21
526 )
527 vsys.ullMemorySize = i.ullVirtualQuantity;
528 else if ( i.strAllocationUnits == "GigaBytes"
529 || i.strAllocationUnits == "GB"
530 || i.strAllocationUnits == "byte * 2^30"
531 )
532 vsys.ullMemorySize = i.ullVirtualQuantity/_1K;//back to MB
533 else
534 throw OVFLogicError(N_("Error reading \"%s\": Invalid allocation unit \"%s\" specified with memory size item, line %d"),
535 m_strPath.c_str(),
536 i.strAllocationUnits.c_str(),
537 i.m_iLineNumber);
538 break;
539
540 case ResourceType_IDEController: // 5
541 {
542 /* <Item>
543 <rasd:Caption>ideController0</rasd:Caption>
544 <rasd:Description>IDE Controller</rasd:Description>
545 <rasd:InstanceId>5</rasd:InstanceId>
546 <rasd:ResourceType>5</rasd:ResourceType>
547 <rasd:Address>0</rasd:Address>
548 <rasd:BusNumber>0</rasd:BusNumber>
549 </Item> */
550 HardDiskController hdc;
551 hdc.system = HardDiskController::IDE;
552 hdc.strIdController = i.strInstanceID;
553 hdc.strControllerType = i.strResourceSubType;
554
555 hdc.lAddress = i.lAddress;
556
557 if (!pPrimaryIDEController)
558 // this is the first IDE controller found: then mark it as "primary"
559 hdc.fPrimary = true;
560 else
561 {
562 // this is the second IDE controller found: If VMware exports two
563 // IDE controllers, it seems that they are given an "Address" of 0
564 // an 1, respectively, so assume address=0 means primary controller
565 if ( pPrimaryIDEController->lAddress == 0
566 && hdc.lAddress == 1
567 )
568 {
569 pPrimaryIDEController->fPrimary = true;
570 hdc.fPrimary = false;
571 }
572 else if ( pPrimaryIDEController->lAddress == 1
573 && hdc.lAddress == 0
574 )
575 {
576 pPrimaryIDEController->fPrimary = false;
577 hdc.fPrimary = false;
578 }
579 else
580 // then we really can't tell, just hope for the best
581 hdc.fPrimary = false;
582 }
583
584 vsys.mapControllers[i.strInstanceID] = hdc;
585 if (!pPrimaryIDEController)
586 pPrimaryIDEController = &vsys.mapControllers[i.strInstanceID];
587 break;
588 }
589
590 case ResourceType_ParallelSCSIHBA: // 6 SCSI controller
591 {
592 /* <Item>
593 <rasd:Caption>SCSI Controller 0 - LSI Logic</rasd:Caption>
594 <rasd:Description>SCI Controller</rasd:Description>
595 <rasd:ElementName>SCSI controller</rasd:ElementName>
596 <rasd:InstanceID>4</rasd:InstanceID>
597 <rasd:ResourceSubType>LsiLogic</rasd:ResourceSubType>
598 <rasd:ResourceType>6</rasd:ResourceType>
599 </Item> */
600 HardDiskController hdc;
601 hdc.system = HardDiskController::SCSI;
602 hdc.strIdController = i.strInstanceID;
603 hdc.strControllerType = i.strResourceSubType;
604
605 vsys.mapControllers[i.strInstanceID] = hdc;
606 break;
607 }
608
609 case ResourceType_EthernetAdapter: // 10
610 {
611 /* <Item>
612 <rasd:Caption>Ethernet adapter on 'Bridged'</rasd:Caption>
613 <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
614 <rasd:Connection>Bridged</rasd:Connection>
615 <rasd:InstanceID>6</rasd:InstanceID>
616 <rasd:ResourceType>10</rasd:ResourceType>
617 <rasd:ResourceSubType>E1000</rasd:ResourceSubType>
618 </Item>
619
620 OVF spec DSP 0243 page 21:
621 "For an Ethernet adapter, this specifies the abstract network connection name
622 for the virtual machine. All Ethernet adapters that specify the same abstract
623 network connection name within an OVF package shall be deployed on the same
624 network. The abstract network connection name shall be listed in the NetworkSection
625 at the outermost envelope level." */
626
627 // only store the name
628 EthernetAdapter ea;
629 ea.strAdapterType = i.strResourceSubType;
630 ea.strNetworkName = i.strConnection;
631 vsys.llEthernetAdapters.push_back(ea);
632 break;
633 }
634
635 case ResourceType_FloppyDrive: // 14
636 vsys.fHasFloppyDrive = true; // we have no additional information
637 break;
638
639 case ResourceType_CDDrive: // 15
640 /* <Item ovf:required="false">
641 <rasd:Caption>cdrom1</rasd:Caption>
642 <rasd:InstanceId>7</rasd:InstanceId>
643 <rasd:ResourceType>15</rasd:ResourceType>
644 <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
645 <rasd:Parent>5</rasd:Parent>
646 <rasd:AddressOnParent>0</rasd:AddressOnParent>
647 </Item> */
648 // I tried to see what happens if I set an ISO for the CD-ROM in VMware Workstation,
649 // but then the ovftool dies with "Device backing not supported". So I guess if
650 // VMware can't export ISOs, then we don't need to be able to import them right now.
651 vsys.fHasCdromDrive = true; // we have no additional information
652 break;
653
654 case ResourceType_HardDisk: // 17
655 // handled separately in second loop below
656 break;
657
658 case ResourceType_OtherStorageDevice: // 20 SATA controller
659 {
660 /* <Item>
661 <rasd:Description>SATA Controller</rasd:Description>
662 <rasd:Caption>sataController0</rasd:Caption>
663 <rasd:InstanceID>4</rasd:InstanceID>
664 <rasd:ResourceType>20</rasd:ResourceType>
665 <rasd:ResourceSubType>AHCI</rasd:ResourceSubType>
666 <rasd:Address>0</rasd:Address>
667 <rasd:BusNumber>0</rasd:BusNumber>
668 </Item> */
669 if ( i.strResourceSubType.compare("AHCI", RTCString::CaseInsensitive) == 0
670 || i.strResourceSubType.compare("vmware.sata.ahci", RTCString::CaseInsensitive) == 0)
671 {
672 HardDiskController hdc;
673 hdc.system = HardDiskController::SATA;
674 hdc.strIdController = i.strInstanceID;
675 hdc.strControllerType = i.strResourceSubType;
676
677 vsys.mapControllers[i.strInstanceID] = hdc;
678 }
679 else if ( i.strResourceSubType.compare("VirtioSCSI", RTCString::CaseInsensitive) == 0
680 || i.strResourceSubType.compare("virtio-scsi", RTCString::CaseInsensitive) == 0 )
681 {
682 HardDiskController hdc;
683 hdc.system = HardDiskController::VIRTIOSCSI; /**< r=klaus: GUI needs to learn about this in the import dialog, currently shown as "Unknown Hardware Item". */
684 hdc.strIdController = i.strInstanceID;
685 //<rasd:ResourceSubType>VirtioSCSI</rasd:ResourceSubType>
686 hdc.strControllerType = i.strResourceSubType;
687 vsys.mapControllers[i.strInstanceID] = hdc;
688 }
689 else
690 throw OVFLogicError(N_("Error reading \"%s\": Host resource of type \"Other Storage Device (%d)\" is supported with SATA AHCI or virtio-scsi controllers only, line %d (subtype:%s)"),
691 m_strPath.c_str(),
692 ResourceType_OtherStorageDevice,
693 i.m_iLineNumber, i.strResourceSubType.c_str() );
694 break;
695 }
696
697 case ResourceType_USBController: // 23
698 /* <Item ovf:required="false">
699 <rasd:Caption>usb</rasd:Caption>
700 <rasd:Description>USB Controller</rasd:Description>
701 <rasd:InstanceId>3</rasd:InstanceId>
702 <rasd:ResourceType>23</rasd:ResourceType>
703 <rasd:Address>0</rasd:Address>
704 <rasd:BusNumber>0</rasd:BusNumber>
705 </Item> */
706 vsys.fHasUsbController = true; // we have no additional information
707 break;
708
709 case ResourceType_SoundCard: // 35
710 /* <Item ovf:required="false">
711 <rasd:Caption>sound</rasd:Caption>
712 <rasd:Description>Sound Card</rasd:Description>
713 <rasd:InstanceId>10</rasd:InstanceId>
714 <rasd:ResourceType>35</rasd:ResourceType>
715 <rasd:ResourceSubType>ensoniq1371</rasd:ResourceSubType>
716 <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
717 <rasd:AddressOnParent>3</rasd:AddressOnParent>
718 </Item> */
719 vsys.strSoundCardType = i.strResourceSubType;
720 break;
721
722 default:
723 {
724 /* If this unknown resource type isn't required, we simply skip it. */
725 if (i.fResourceRequired)
726 {
727 throw OVFLogicError(N_("Error reading \"%s\": Unknown resource type %d in hardware item, line %d"),
728 m_strPath.c_str(),
729 i.resourceType,
730 i.m_iLineNumber);
731 }
732 }
733 } // end switch
734 }
735
736 // now run through the items for a second time, but handle only
737 // hard disk images; otherwise the code would fail if a hard
738 // disk image appears in the OVF before its hard disk controller
739 for (itH = vsys.vecHardwareItems.begin(); itH != vsys.vecHardwareItems.end(); ++itH)
740 {
741 const VirtualHardwareItem &i = **itH;
742
743 // do some analysis
744 switch (i.resourceType)
745 {
746 case ResourceType_CDDrive: // 15
747 /* <Item ovf:required="false">
748 <rasd:Caption>cdrom1</rasd:Caption>
749 <rasd:InstanceId>7</rasd:InstanceId>
750 <rasd:ResourceType>15</rasd:ResourceType>
751 <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
752 <rasd:Parent>5</rasd:Parent>
753 <rasd:AddressOnParent>0</rasd:AddressOnParent>
754 </Item> */
755 case ResourceType_HardDisk: // 17
756 {
757 /* <Item>
758 <rasd:Caption>Harddisk 1</rasd:Caption>
759 <rasd:Description>HD</rasd:Description>
760 <rasd:ElementName>Hard Disk</rasd:ElementName>
761 <rasd:HostResource>ovf://disk/lamp</rasd:HostResource>
762 <rasd:InstanceID>5</rasd:InstanceID>
763 <rasd:Parent>4</rasd:Parent>
764 <rasd:ResourceType>17</rasd:ResourceType>
765 </Item> */
766
767 // look up the hard disk controller element whose InstanceID equals our Parent;
768 // this is how the connection is specified in OVF
769 ControllersMap::const_iterator it = vsys.mapControllers.find(i.strParent);
770 if (it == vsys.mapControllers.end())
771 throw OVFLogicError(N_("Error reading \"%s\": Disk item with instance ID \"%s\" specifies invalid parent \"%s\", line %d"),
772 m_strPath.c_str(),
773 i.strInstanceID.c_str(),
774 i.strParent.c_str(),
775 i.m_iLineNumber);
776
777 VirtualDisk vd;
778 vd.strIdController = i.strParent;
779 i.strAddressOnParent.toInt(vd.ulAddressOnParent);
780 // ovf://disk/lamp
781 // 123456789012345
782 if (i.strHostResource.startsWith("ovf://disk/"))
783 vd.strDiskId = i.strHostResource.substr(11);
784 else if (i.strHostResource.startsWith("ovf:/disk/"))
785 vd.strDiskId = i.strHostResource.substr(10);
786 else if (i.strHostResource.startsWith("/disk/"))
787 vd.strDiskId = i.strHostResource.substr(6);
788
789 //the error may be missed for CD, because CD can be empty
790 if ((vd.strDiskId.isEmpty() || (m_mapDisks.find(vd.strDiskId) == m_mapDisks.end()))
791 && i.resourceType == ResourceType_HardDisk)
792 throw OVFLogicError(N_("Error reading \"%s\": Disk item with instance ID \"%s\" specifies invalid host resource \"%s\", line %d"),
793 m_strPath.c_str(),
794 i.strInstanceID.c_str(),
795 i.strHostResource.c_str(),
796 i.m_iLineNumber);
797
798 vsys.mapVirtualDisks[vd.strDiskId] = vd;
799 break;
800 }
801 default:
802 break;
803 }
804 }
805 }
806 else if ( !strcmp(pcszElemName, "OperatingSystemSection")
807 || !strcmp(pcszTypeAttr, "ovf:OperatingSystemSection_Type")
808 )
809 {
810 uint64_t cimos64;
811 if (!(pelmThis->getAttributeValue("id", cimos64)))
812 throw OVFLogicError(N_("Error reading \"%s\": missing or invalid 'ovf:id' attribute in operating system section element, line %d"),
813 m_strPath.c_str(),
814 pelmThis->getLineNumber());
815
816 vsys.cimos = (CIMOSType_T)cimos64;
817 const xml::ElementNode *pelmCIMOSDescription;
818 if ((pelmCIMOSDescription = pelmThis->findChildElement("Description")))
819 vsys.strCimosDesc = pelmCIMOSDescription->getValueN(RT_XML_CONTENT_SMALL);
820
821 const xml::ElementNode *pelmVBoxOSType;
822 if ((pelmVBoxOSType = pelmThis->findChildElementNS("vbox", // namespace
823 "OSType"))) // element name
824 vsys.strTypeVBox = pelmVBoxOSType->getValueN(RT_XML_CONTENT_SMALL);
825 }
826 else if ( (!strcmp(pcszElemName, "AnnotationSection"))
827 || (!strcmp(pcszTypeAttr, "ovf:AnnotationSection_Type"))
828 )
829 {
830 const xml::ElementNode *pelmAnnotation;
831 if ((pelmAnnotation = pelmThis->findChildElement("Annotation")))
832 vsys.strDescription = pelmAnnotation->getValueN(RT_XML_CONTENT_SMALL);
833 }
834 }
835}
836
837void VirtualHardwareItem::fillItem(const xml::ElementNode *item)
838{
839 xml::NodesLoop loopItemChildren(*item);// all child elements
840 const xml::ElementNode *pelmItemChild;
841 while ((pelmItemChild = loopItemChildren.forAllNodes()))
842 {
843 const char *pcszItemChildName = pelmItemChild->getName();
844 if (!strcmp(pcszItemChildName, "Description"))
845 strDescription = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
846 else if (!strcmp(pcszItemChildName, "Caption"))
847 strCaption = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
848 else if (!strcmp(pcszItemChildName, "ElementName"))
849 strElementName = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
850 else if ( !strcmp(pcszItemChildName, "InstanceID")
851 || !strcmp(pcszItemChildName, "InstanceId") )
852 strInstanceID = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
853 else if (!strcmp(pcszItemChildName, "HostResource"))
854 strHostResource = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
855 else if (!strcmp(pcszItemChildName, "ResourceType"))
856 {
857 uint32_t ulType;
858 pelmItemChild->copyValue(ulType);
859 if (ulType > 0xffff)
860 ulType = 0xffff;
861 resourceType = (ResourceType_T)ulType;
862 fResourceRequired = true;
863 const char *pcszAttValue;
864 if (item->getAttributeValueN("required", pcszAttValue, RT_XML_ATTR_TINY))
865 {
866 if (!strcmp(pcszAttValue, "false"))
867 fResourceRequired = false;
868 }
869 }
870 else if (!strcmp(pcszItemChildName, "OtherResourceType"))
871 strOtherResourceType = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
872 else if (!strcmp(pcszItemChildName, "ResourceSubType"))
873 strResourceSubType = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
874 else if (!strcmp(pcszItemChildName, "AutomaticAllocation"))
875 fAutomaticAllocation = (!strcmp(pelmItemChild->getValueN(RT_XML_CONTENT_SMALL), "true")) ? true : false;
876 else if (!strcmp(pcszItemChildName, "AutomaticDeallocation"))
877 fAutomaticDeallocation = (!strcmp(pelmItemChild->getValueN(RT_XML_CONTENT_SMALL), "true")) ? true : false;
878 else if (!strcmp(pcszItemChildName, "Parent"))
879 strParent = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
880 else if (!strcmp(pcszItemChildName, "Connection"))
881 strConnection = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
882 else if (!strcmp(pcszItemChildName, "Address"))
883 {
884 strAddress = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
885 pelmItemChild->copyValue(lAddress);
886 }
887 else if (!strcmp(pcszItemChildName, "AddressOnParent"))
888 strAddressOnParent = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
889 else if (!strcmp(pcszItemChildName, "AllocationUnits"))
890 strAllocationUnits = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
891 else if (!strcmp(pcszItemChildName, "VirtualQuantity"))
892 pelmItemChild->copyValue(ullVirtualQuantity);
893 else if (!strcmp(pcszItemChildName, "Reservation"))
894 pelmItemChild->copyValue(ullReservation);
895 else if (!strcmp(pcszItemChildName, "Limit"))
896 pelmItemChild->copyValue(ullLimit);
897 else if (!strcmp(pcszItemChildName, "Weight"))
898 pelmItemChild->copyValue(ullWeight);
899 else if (!strcmp(pcszItemChildName, "ConsumerVisibility"))
900 strConsumerVisibility = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
901 else if (!strcmp(pcszItemChildName, "MappingBehavior"))
902 strMappingBehavior = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
903 else if (!strcmp(pcszItemChildName, "PoolID"))
904 strPoolID = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
905 else if (!strcmp(pcszItemChildName, "BusNumber"))
906 pelmItemChild->copyValue(ulBusNumber);
907// else if (pelmItemChild->getPrefix() == NULL
908// || strcmp(pelmItemChild->getPrefix(), "vmw"))
909// throw OVFLogicError(N_("Unknown element '%s' under Item element, line %d"),
910// pcszItemChildName,
911// m_iLineNumber);
912 }
913}
914
915void VirtualHardwareItem::_checkConsistencyAndCompliance() RT_THROW(OVFLogicError)
916{
917 RTCString name = getItemName();
918 if (resourceType == 0)
919 throw OVFLogicError(N_("Empty element ResourceType under %s element, line %d. see DMTF Schema Documentation %s"),
920 name.c_str(), m_iLineNumber, DTMF_SPECS_URI);
921
922 /* Don't be too uptight about the strInstanceID value. There are OVAs out
923 there which have InstanceID="%iid%" for memory for instance, which is
924 no good reason for not being able to process them. bugref:8997 */
925 if (strInstanceID.isEmpty())
926 {
927 if ( resourceType == ResourceType_IDEController
928 || resourceType == ResourceType_OtherStorageDevice
929 || resourceType == ResourceType_ParallelSCSIHBA
930 || resourceType == ResourceType_iSCSIHBA //??
931 || resourceType == ResourceType_IBHCA ) //??
932 throw OVFLogicError(N_("Element InstanceID is absent under %s element, line %d. see DMTF Schema Documentation %s"),
933 name.c_str(), m_iLineNumber, DTMF_SPECS_URI);
934 else
935 LogRel(("OVFREADER: Warning: Ignoring missing or invalid InstanceID under element %s, line %u\n",
936 name.c_str(), m_iLineNumber));
937 }
938}
939
940void StorageItem::fillItem(const xml::ElementNode *item)
941{
942 VirtualHardwareItem::fillItem(item);
943
944 xml::NodesLoop loopItemChildren(*item);// all child elements
945 const xml::ElementNode *pelmItemChild;
946 while ((pelmItemChild = loopItemChildren.forAllNodes()))
947 {
948 const char *pcszItemChildName = pelmItemChild->getName();
949 if (!strcmp(pcszItemChildName, "HostExtentName"))
950 strHostExtentName = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
951 else if (!strcmp(pcszItemChildName, "OtherHostExtentNameFormat"))
952 strOtherHostExtentNameFormat = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
953 else if (!strcmp(pcszItemChildName, "OtherHostExtentNameNamespace"))
954 strOtherHostExtentNameNamespace = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
955 else if (!strcmp(pcszItemChildName, "VirtualQuantityUnits"))
956 strVirtualQuantityUnits = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
957 else if (!strcmp(pcszItemChildName, "Access"))
958 {
959 uint32_t temp;
960 pelmItemChild->copyValue(temp);
961 accessType = (StorageAccessType_T)temp;
962 }
963 else if (!strcmp(pcszItemChildName, "HostExtentNameFormat"))
964 {
965 }
966 else if (!strcmp(pcszItemChildName, "HostExtentNameNamespace"))
967 {
968 }
969 else if (!strcmp(pcszItemChildName, "HostExtentStartingAddress"))
970 {
971 }
972 else if (!strcmp(pcszItemChildName, "HostResourceBlockSize"))
973 {
974 int64_t temp;
975 pelmItemChild->copyValue(temp);
976 hostResourceBlockSize = temp;
977 }
978 else if (!strcmp(pcszItemChildName, "Limit"))
979 {
980 int64_t temp;
981 pelmItemChild->copyValue(temp);
982 limit = temp;
983 }
984 else if (!strcmp(pcszItemChildName, "Reservation"))
985 {
986 int64_t temp;
987 pelmItemChild->copyValue(temp);
988 reservation = temp;
989 }
990 else if (!strcmp(pcszItemChildName, "VirtualQuantity"))
991 {
992 int64_t temp;
993 pelmItemChild->copyValue(temp);
994 virtualQuantity = temp;
995 }
996 else if (!strcmp(pcszItemChildName, "VirtualResourceBlockSize"))
997 {
998 int64_t temp;
999 pelmItemChild->copyValue(temp);
1000 virtualResourceBlockSize = temp;
1001 }
1002 }
1003}
1004
1005
1006void StorageItem::_checkConsistencyAndCompliance() RT_THROW(OVFLogicError)
1007{
1008 VirtualHardwareItem::_checkConsistencyAndCompliance();
1009
1010 RTCString name = getItemName();
1011
1012 if (accessType == StorageAccessType_Unknown)
1013 {
1014 //throw OVFLogicError(N_("Access type is unknown under %s element, line %d"),
1015 // name.c_str(), m_iLineNumber);
1016 }
1017
1018 if (hostResourceBlockSize <= 0 && reservation > 0)
1019 {
1020 throw OVFLogicError(N_("Element HostResourceBlockSize is absent under %s element, line %d. "
1021 "see DMTF Schema Documentation %s"),
1022 name.c_str(), m_iLineNumber, DTMF_SPECS_URI);
1023 }
1024
1025 if (virtualResourceBlockSize <= 0 && virtualQuantity > 0)
1026 {
1027 throw OVFLogicError(N_("Element VirtualResourceBlockSize is absent under %s element, line %d. "
1028 "see DMTF Schema Documentation %s"),
1029 name.c_str(), m_iLineNumber, DTMF_SPECS_URI);
1030 }
1031
1032 if (virtualQuantity > 0 && strVirtualQuantityUnits.isEmpty())
1033 {
1034 throw OVFLogicError(N_("Element VirtualQuantityUnits is absent under %s element, line %d. "
1035 "see DMTF Schema Documentation %s"),
1036 name.c_str(), m_iLineNumber, DTMF_SPECS_URI);
1037 }
1038
1039 if (virtualResourceBlockSize <= 1 &&
1040 strVirtualQuantityUnits.compare(RTCString("count"), RTCString::CaseInsensitive) == 0
1041 )
1042 {
1043 throw OVFLogicError(N_("Element VirtualQuantityUnits is set to \"count\" "
1044 "while VirtualResourceBlockSize is set to 1. "
1045 "under %s element, line %d. "
1046 "It's needed to change on \"byte\". "
1047 "see DMTF Schema Documentation %s"),
1048 name.c_str(), m_iLineNumber, DTMF_SPECS_URI);
1049 }
1050}
1051
1052void EthernetPortItem::fillItem(const xml::ElementNode *item)
1053{
1054 VirtualHardwareItem::fillItem(item);
1055
1056 xml::NodesLoop loopItemChildren(*item);// all child elements
1057 const xml::ElementNode *pelmItemChild;
1058 while ((pelmItemChild = loopItemChildren.forAllNodes()))
1059 {
1060 }
1061}
1062
1063void EthernetPortItem::_checkConsistencyAndCompliance() RT_THROW(OVFLogicError)
1064{
1065 VirtualHardwareItem::_checkConsistencyAndCompliance();
1066}
1067
1068////////////////////////////////////////////////////////////////////////////////
1069//
1070// Errors
1071//
1072////////////////////////////////////////////////////////////////////////////////
1073
1074OVFLogicError::OVFLogicError(const char *aFormat, ...)
1075{
1076 char *pszNewMsg;
1077 va_list args;
1078 va_start(args, aFormat);
1079 RTStrAPrintfV(&pszNewMsg, aFormat, args);
1080 setWhat(pszNewMsg);
1081 RTStrFree(pszNewMsg);
1082 va_end(args);
1083}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette