VirtualBox

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

Last change on this file since 101200 was 99739, checked in by vboxsync, 13 months ago

*: doxygen corrections (mostly about removing @returns from functions returning void).

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

© 2023 Oracle
ContactPrivacy policyTerms of Use