VirtualBox

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

Last change on this file since 99604 was 99604, checked in by vboxsync, 17 months ago

bugref:10314. bugref:10278. Reverted VSD RAM unit to bytes and fixed other places where MB unit was used.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 50.0 KB
Line 
1/* $Id: ovfreader.cpp 99604 2023-05-04 13:53:06Z 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 /* It's alway stored in bytes in VSD according to the old internal agreement within the team */
524 if ( i.strAllocationUnits == "MegaBytes" // found in OVF created by OVF toolkit
525 || i.strAllocationUnits == "MB" // found in MS docs
526 || i.strAllocationUnits == "byte * 2^20" // suggested by OVF spec DSP0243 page 21
527 )
528 vsys.ullMemorySize = i.ullVirtualQuantity * _1M;
529 else if ( i.strAllocationUnits == "GigaBytes"
530 || i.strAllocationUnits == "GB"
531 || i.strAllocationUnits == "byte * 2^30"
532 )
533 vsys.ullMemorySize = i.ullVirtualQuantity * _1G;
534 else
535 throw OVFLogicError(N_("Error reading \"%s\": Invalid allocation unit \"%s\" specified with memory size item, line %d"),
536 m_strPath.c_str(),
537 i.strAllocationUnits.c_str(),
538 i.m_iLineNumber);
539 break;
540
541 case ResourceType_IDEController: // 5
542 {
543 /* <Item>
544 <rasd:Caption>ideController0</rasd:Caption>
545 <rasd:Description>IDE Controller</rasd:Description>
546 <rasd:InstanceId>5</rasd:InstanceId>
547 <rasd:ResourceType>5</rasd:ResourceType>
548 <rasd:Address>0</rasd:Address>
549 <rasd:BusNumber>0</rasd:BusNumber>
550 </Item> */
551 HardDiskController hdc;
552 hdc.system = HardDiskController::IDE;
553 hdc.strIdController = i.strInstanceID;
554 hdc.strControllerType = i.strResourceSubType;
555
556 hdc.lAddress = i.lAddress;
557
558 if (!pPrimaryIDEController)
559 // this is the first IDE controller found: then mark it as "primary"
560 hdc.fPrimary = true;
561 else
562 {
563 // this is the second IDE controller found: If VMware exports two
564 // IDE controllers, it seems that they are given an "Address" of 0
565 // an 1, respectively, so assume address=0 means primary controller
566 if ( pPrimaryIDEController->lAddress == 0
567 && hdc.lAddress == 1
568 )
569 {
570 pPrimaryIDEController->fPrimary = true;
571 hdc.fPrimary = false;
572 }
573 else if ( pPrimaryIDEController->lAddress == 1
574 && hdc.lAddress == 0
575 )
576 {
577 pPrimaryIDEController->fPrimary = false;
578 hdc.fPrimary = false;
579 }
580 else
581 // then we really can't tell, just hope for the best
582 hdc.fPrimary = false;
583 }
584
585 vsys.mapControllers[i.strInstanceID] = hdc;
586 if (!pPrimaryIDEController)
587 pPrimaryIDEController = &vsys.mapControllers[i.strInstanceID];
588 break;
589 }
590
591 case ResourceType_ParallelSCSIHBA: // 6 SCSI controller
592 {
593 /* <Item>
594 <rasd:Caption>SCSI Controller 0 - LSI Logic</rasd:Caption>
595 <rasd:Description>SCI Controller</rasd:Description>
596 <rasd:ElementName>SCSI controller</rasd:ElementName>
597 <rasd:InstanceID>4</rasd:InstanceID>
598 <rasd:ResourceSubType>LsiLogic</rasd:ResourceSubType>
599 <rasd:ResourceType>6</rasd:ResourceType>
600 </Item> */
601 HardDiskController hdc;
602 hdc.system = HardDiskController::SCSI;
603 hdc.strIdController = i.strInstanceID;
604 hdc.strControllerType = i.strResourceSubType;
605
606 vsys.mapControllers[i.strInstanceID] = hdc;
607 break;
608 }
609
610 case ResourceType_EthernetAdapter: // 10
611 {
612 /* <Item>
613 <rasd:Caption>Ethernet adapter on 'Bridged'</rasd:Caption>
614 <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
615 <rasd:Connection>Bridged</rasd:Connection>
616 <rasd:InstanceID>6</rasd:InstanceID>
617 <rasd:ResourceType>10</rasd:ResourceType>
618 <rasd:ResourceSubType>E1000</rasd:ResourceSubType>
619 </Item>
620
621 OVF spec DSP 0243 page 21:
622 "For an Ethernet adapter, this specifies the abstract network connection name
623 for the virtual machine. All Ethernet adapters that specify the same abstract
624 network connection name within an OVF package shall be deployed on the same
625 network. The abstract network connection name shall be listed in the NetworkSection
626 at the outermost envelope level." */
627
628 // only store the name
629 EthernetAdapter ea;
630 ea.strAdapterType = i.strResourceSubType;
631 ea.strNetworkName = i.strConnection;
632 vsys.llEthernetAdapters.push_back(ea);
633 break;
634 }
635
636 case ResourceType_FloppyDrive: // 14
637 vsys.fHasFloppyDrive = true; // we have no additional information
638 break;
639
640 case ResourceType_CDDrive: // 15
641 /* <Item ovf:required="false">
642 <rasd:Caption>cdrom1</rasd:Caption>
643 <rasd:InstanceId>7</rasd:InstanceId>
644 <rasd:ResourceType>15</rasd:ResourceType>
645 <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
646 <rasd:Parent>5</rasd:Parent>
647 <rasd:AddressOnParent>0</rasd:AddressOnParent>
648 </Item> */
649 // I tried to see what happens if I set an ISO for the CD-ROM in VMware Workstation,
650 // but then the ovftool dies with "Device backing not supported". So I guess if
651 // VMware can't export ISOs, then we don't need to be able to import them right now.
652 vsys.fHasCdromDrive = true; // we have no additional information
653 break;
654
655 case ResourceType_HardDisk: // 17
656 // handled separately in second loop below
657 break;
658
659 case ResourceType_OtherStorageDevice: // 20 SATA controller
660 {
661 /* <Item>
662 <rasd:Description>SATA Controller</rasd:Description>
663 <rasd:Caption>sataController0</rasd:Caption>
664 <rasd:InstanceID>4</rasd:InstanceID>
665 <rasd:ResourceType>20</rasd:ResourceType>
666 <rasd:ResourceSubType>AHCI</rasd:ResourceSubType>
667 <rasd:Address>0</rasd:Address>
668 <rasd:BusNumber>0</rasd:BusNumber>
669 </Item> */
670 if ( i.strResourceSubType.compare("AHCI", RTCString::CaseInsensitive) == 0
671 || i.strResourceSubType.compare("vmware.sata.ahci", RTCString::CaseInsensitive) == 0)
672 {
673 HardDiskController hdc;
674 hdc.system = HardDiskController::SATA;
675 hdc.strIdController = i.strInstanceID;
676 hdc.strControllerType = i.strResourceSubType;
677
678 vsys.mapControllers[i.strInstanceID] = hdc;
679 }
680 else if ( i.strResourceSubType.compare("VirtioSCSI", RTCString::CaseInsensitive) == 0
681 || i.strResourceSubType.compare("virtio-scsi", RTCString::CaseInsensitive) == 0 )
682 {
683 HardDiskController hdc;
684 hdc.system = HardDiskController::VIRTIOSCSI; /**< r=klaus: GUI needs to learn about this in the import dialog, currently shown as "Unknown Hardware Item". */
685 hdc.strIdController = i.strInstanceID;
686 //<rasd:ResourceSubType>VirtioSCSI</rasd:ResourceSubType>
687 hdc.strControllerType = i.strResourceSubType;
688 vsys.mapControllers[i.strInstanceID] = hdc;
689 }
690 else
691 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)"),
692 m_strPath.c_str(),
693 ResourceType_OtherStorageDevice,
694 i.m_iLineNumber, i.strResourceSubType.c_str() );
695 break;
696 }
697
698 case ResourceType_USBController: // 23
699 /* <Item ovf:required="false">
700 <rasd:Caption>usb</rasd:Caption>
701 <rasd:Description>USB Controller</rasd:Description>
702 <rasd:InstanceId>3</rasd:InstanceId>
703 <rasd:ResourceType>23</rasd:ResourceType>
704 <rasd:Address>0</rasd:Address>
705 <rasd:BusNumber>0</rasd:BusNumber>
706 </Item> */
707 vsys.fHasUsbController = true; // we have no additional information
708 break;
709
710 case ResourceType_SoundCard: // 35
711 /* <Item ovf:required="false">
712 <rasd:Caption>sound</rasd:Caption>
713 <rasd:Description>Sound Card</rasd:Description>
714 <rasd:InstanceId>10</rasd:InstanceId>
715 <rasd:ResourceType>35</rasd:ResourceType>
716 <rasd:ResourceSubType>ensoniq1371</rasd:ResourceSubType>
717 <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
718 <rasd:AddressOnParent>3</rasd:AddressOnParent>
719 </Item> */
720 vsys.strSoundCardType = i.strResourceSubType;
721 break;
722
723 default:
724 {
725 /* If this unknown resource type isn't required, we simply skip it. */
726 if (i.fResourceRequired)
727 {
728 throw OVFLogicError(N_("Error reading \"%s\": Unknown resource type %d in hardware item, line %d"),
729 m_strPath.c_str(),
730 i.resourceType,
731 i.m_iLineNumber);
732 }
733 }
734 } // end switch
735 }
736
737 // now run through the items for a second time, but handle only
738 // hard disk images; otherwise the code would fail if a hard
739 // disk image appears in the OVF before its hard disk controller
740 for (itH = vsys.vecHardwareItems.begin(); itH != vsys.vecHardwareItems.end(); ++itH)
741 {
742 const VirtualHardwareItem &i = **itH;
743
744 // do some analysis
745 switch (i.resourceType)
746 {
747 case ResourceType_CDDrive: // 15
748 /* <Item ovf:required="false">
749 <rasd:Caption>cdrom1</rasd:Caption>
750 <rasd:InstanceId>7</rasd:InstanceId>
751 <rasd:ResourceType>15</rasd:ResourceType>
752 <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
753 <rasd:Parent>5</rasd:Parent>
754 <rasd:AddressOnParent>0</rasd:AddressOnParent>
755 </Item> */
756 case ResourceType_HardDisk: // 17
757 {
758 /* <Item>
759 <rasd:Caption>Harddisk 1</rasd:Caption>
760 <rasd:Description>HD</rasd:Description>
761 <rasd:ElementName>Hard Disk</rasd:ElementName>
762 <rasd:HostResource>ovf://disk/lamp</rasd:HostResource>
763 <rasd:InstanceID>5</rasd:InstanceID>
764 <rasd:Parent>4</rasd:Parent>
765 <rasd:ResourceType>17</rasd:ResourceType>
766 </Item> */
767
768 // look up the hard disk controller element whose InstanceID equals our Parent;
769 // this is how the connection is specified in OVF
770 ControllersMap::const_iterator it = vsys.mapControllers.find(i.strParent);
771 if (it == vsys.mapControllers.end())
772 throw OVFLogicError(N_("Error reading \"%s\": Disk item with instance ID \"%s\" specifies invalid parent \"%s\", line %d"),
773 m_strPath.c_str(),
774 i.strInstanceID.c_str(),
775 i.strParent.c_str(),
776 i.m_iLineNumber);
777
778 VirtualDisk vd;
779 vd.strIdController = i.strParent;
780 i.strAddressOnParent.toInt(vd.ulAddressOnParent);
781 // ovf://disk/lamp
782 // 123456789012345
783 if (i.strHostResource.startsWith("ovf://disk/"))
784 vd.strDiskId = i.strHostResource.substr(11);
785 else if (i.strHostResource.startsWith("ovf:/disk/"))
786 vd.strDiskId = i.strHostResource.substr(10);
787 else if (i.strHostResource.startsWith("/disk/"))
788 vd.strDiskId = i.strHostResource.substr(6);
789
790 //the error may be missed for CD, because CD can be empty
791 if ((vd.strDiskId.isEmpty() || (m_mapDisks.find(vd.strDiskId) == m_mapDisks.end()))
792 && i.resourceType == ResourceType_HardDisk)
793 throw OVFLogicError(N_("Error reading \"%s\": Disk item with instance ID \"%s\" specifies invalid host resource \"%s\", line %d"),
794 m_strPath.c_str(),
795 i.strInstanceID.c_str(),
796 i.strHostResource.c_str(),
797 i.m_iLineNumber);
798
799 vsys.mapVirtualDisks[vd.strDiskId] = vd;
800 break;
801 }
802 default:
803 break;
804 }
805 }
806 }
807 else if ( !strcmp(pcszElemName, "OperatingSystemSection")
808 || !strcmp(pcszTypeAttr, "ovf:OperatingSystemSection_Type")
809 )
810 {
811 uint64_t cimos64;
812 if (!(pelmThis->getAttributeValue("id", cimos64)))
813 throw OVFLogicError(N_("Error reading \"%s\": missing or invalid 'ovf:id' attribute in operating system section element, line %d"),
814 m_strPath.c_str(),
815 pelmThis->getLineNumber());
816
817 vsys.cimos = (CIMOSType_T)cimos64;
818 const xml::ElementNode *pelmCIMOSDescription;
819 if ((pelmCIMOSDescription = pelmThis->findChildElement("Description")))
820 vsys.strCimosDesc = pelmCIMOSDescription->getValueN(RT_XML_CONTENT_SMALL);
821
822 const xml::ElementNode *pelmVBoxOSType;
823 if ((pelmVBoxOSType = pelmThis->findChildElementNS("vbox", // namespace
824 "OSType"))) // element name
825 vsys.strTypeVBox = pelmVBoxOSType->getValueN(RT_XML_CONTENT_SMALL);
826 }
827 else if ( (!strcmp(pcszElemName, "AnnotationSection"))
828 || (!strcmp(pcszTypeAttr, "ovf:AnnotationSection_Type"))
829 )
830 {
831 const xml::ElementNode *pelmAnnotation;
832 if ((pelmAnnotation = pelmThis->findChildElement("Annotation")))
833 vsys.strDescription = pelmAnnotation->getValueN(RT_XML_CONTENT_SMALL);
834 }
835 }
836}
837
838void VirtualHardwareItem::fillItem(const xml::ElementNode *item)
839{
840 xml::NodesLoop loopItemChildren(*item);// all child elements
841 const xml::ElementNode *pelmItemChild;
842 while ((pelmItemChild = loopItemChildren.forAllNodes()))
843 {
844 const char *pcszItemChildName = pelmItemChild->getName();
845 if (!strcmp(pcszItemChildName, "Description"))
846 strDescription = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
847 else if (!strcmp(pcszItemChildName, "Caption"))
848 strCaption = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
849 else if (!strcmp(pcszItemChildName, "ElementName"))
850 strElementName = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
851 else if ( !strcmp(pcszItemChildName, "InstanceID")
852 || !strcmp(pcszItemChildName, "InstanceId") )
853 strInstanceID = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
854 else if (!strcmp(pcszItemChildName, "HostResource"))
855 strHostResource = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
856 else if (!strcmp(pcszItemChildName, "ResourceType"))
857 {
858 uint32_t ulType;
859 pelmItemChild->copyValue(ulType);
860 if (ulType > 0xffff)
861 ulType = 0xffff;
862 resourceType = (ResourceType_T)ulType;
863 fResourceRequired = true;
864 const char *pcszAttValue;
865 if (item->getAttributeValueN("required", pcszAttValue, RT_XML_ATTR_TINY))
866 {
867 if (!strcmp(pcszAttValue, "false"))
868 fResourceRequired = false;
869 }
870 }
871 else if (!strcmp(pcszItemChildName, "OtherResourceType"))
872 strOtherResourceType = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
873 else if (!strcmp(pcszItemChildName, "ResourceSubType"))
874 strResourceSubType = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
875 else if (!strcmp(pcszItemChildName, "AutomaticAllocation"))
876 fAutomaticAllocation = (!strcmp(pelmItemChild->getValueN(RT_XML_CONTENT_SMALL), "true")) ? true : false;
877 else if (!strcmp(pcszItemChildName, "AutomaticDeallocation"))
878 fAutomaticDeallocation = (!strcmp(pelmItemChild->getValueN(RT_XML_CONTENT_SMALL), "true")) ? true : false;
879 else if (!strcmp(pcszItemChildName, "Parent"))
880 strParent = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
881 else if (!strcmp(pcszItemChildName, "Connection"))
882 strConnection = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
883 else if (!strcmp(pcszItemChildName, "Address"))
884 {
885 strAddress = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
886 pelmItemChild->copyValue(lAddress);
887 }
888 else if (!strcmp(pcszItemChildName, "AddressOnParent"))
889 strAddressOnParent = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
890 else if (!strcmp(pcszItemChildName, "AllocationUnits"))
891 strAllocationUnits = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
892 else if (!strcmp(pcszItemChildName, "VirtualQuantity"))
893 pelmItemChild->copyValue(ullVirtualQuantity);
894 else if (!strcmp(pcszItemChildName, "Reservation"))
895 pelmItemChild->copyValue(ullReservation);
896 else if (!strcmp(pcszItemChildName, "Limit"))
897 pelmItemChild->copyValue(ullLimit);
898 else if (!strcmp(pcszItemChildName, "Weight"))
899 pelmItemChild->copyValue(ullWeight);
900 else if (!strcmp(pcszItemChildName, "ConsumerVisibility"))
901 strConsumerVisibility = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
902 else if (!strcmp(pcszItemChildName, "MappingBehavior"))
903 strMappingBehavior = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
904 else if (!strcmp(pcszItemChildName, "PoolID"))
905 strPoolID = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
906 else if (!strcmp(pcszItemChildName, "BusNumber"))
907 pelmItemChild->copyValue(ulBusNumber);
908// else if (pelmItemChild->getPrefix() == NULL
909// || strcmp(pelmItemChild->getPrefix(), "vmw"))
910// throw OVFLogicError(N_("Unknown element '%s' under Item element, line %d"),
911// pcszItemChildName,
912// m_iLineNumber);
913 }
914}
915
916void VirtualHardwareItem::_checkConsistencyAndCompliance() RT_THROW(OVFLogicError)
917{
918 RTCString name = getItemName();
919 if (resourceType == 0)
920 throw OVFLogicError(N_("Empty element ResourceType under %s element, line %d. see DMTF Schema Documentation %s"),
921 name.c_str(), m_iLineNumber, DTMF_SPECS_URI);
922
923 /* Don't be too uptight about the strInstanceID value. There are OVAs out
924 there which have InstanceID="%iid%" for memory for instance, which is
925 no good reason for not being able to process them. bugref:8997 */
926 if (strInstanceID.isEmpty())
927 {
928 if ( resourceType == ResourceType_IDEController
929 || resourceType == ResourceType_OtherStorageDevice
930 || resourceType == ResourceType_ParallelSCSIHBA
931 || resourceType == ResourceType_iSCSIHBA //??
932 || resourceType == ResourceType_IBHCA ) //??
933 throw OVFLogicError(N_("Element InstanceID is absent under %s element, line %d. see DMTF Schema Documentation %s"),
934 name.c_str(), m_iLineNumber, DTMF_SPECS_URI);
935 else
936 LogRel(("OVFREADER: Warning: Ignoring missing or invalid InstanceID under element %s, line %u\n",
937 name.c_str(), m_iLineNumber));
938 }
939}
940
941void StorageItem::fillItem(const xml::ElementNode *item)
942{
943 VirtualHardwareItem::fillItem(item);
944
945 xml::NodesLoop loopItemChildren(*item);// all child elements
946 const xml::ElementNode *pelmItemChild;
947 while ((pelmItemChild = loopItemChildren.forAllNodes()))
948 {
949 const char *pcszItemChildName = pelmItemChild->getName();
950 if (!strcmp(pcszItemChildName, "HostExtentName"))
951 strHostExtentName = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
952 else if (!strcmp(pcszItemChildName, "OtherHostExtentNameFormat"))
953 strOtherHostExtentNameFormat = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
954 else if (!strcmp(pcszItemChildName, "OtherHostExtentNameNamespace"))
955 strOtherHostExtentNameNamespace = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
956 else if (!strcmp(pcszItemChildName, "VirtualQuantityUnits"))
957 strVirtualQuantityUnits = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
958 else if (!strcmp(pcszItemChildName, "Access"))
959 {
960 uint32_t temp;
961 pelmItemChild->copyValue(temp);
962 accessType = (StorageAccessType_T)temp;
963 }
964 else if (!strcmp(pcszItemChildName, "HostExtentNameFormat"))
965 {
966 }
967 else if (!strcmp(pcszItemChildName, "HostExtentNameNamespace"))
968 {
969 }
970 else if (!strcmp(pcszItemChildName, "HostExtentStartingAddress"))
971 {
972 }
973 else if (!strcmp(pcszItemChildName, "HostResourceBlockSize"))
974 {
975 int64_t temp;
976 pelmItemChild->copyValue(temp);
977 hostResourceBlockSize = temp;
978 }
979 else if (!strcmp(pcszItemChildName, "Limit"))
980 {
981 int64_t temp;
982 pelmItemChild->copyValue(temp);
983 limit = temp;
984 }
985 else if (!strcmp(pcszItemChildName, "Reservation"))
986 {
987 int64_t temp;
988 pelmItemChild->copyValue(temp);
989 reservation = temp;
990 }
991 else if (!strcmp(pcszItemChildName, "VirtualQuantity"))
992 {
993 int64_t temp;
994 pelmItemChild->copyValue(temp);
995 virtualQuantity = temp;
996 }
997 else if (!strcmp(pcszItemChildName, "VirtualResourceBlockSize"))
998 {
999 int64_t temp;
1000 pelmItemChild->copyValue(temp);
1001 virtualResourceBlockSize = temp;
1002 }
1003 }
1004}
1005
1006
1007void StorageItem::_checkConsistencyAndCompliance() RT_THROW(OVFLogicError)
1008{
1009 VirtualHardwareItem::_checkConsistencyAndCompliance();
1010
1011 RTCString name = getItemName();
1012
1013 if (accessType == StorageAccessType_Unknown)
1014 {
1015 //throw OVFLogicError(N_("Access type is unknown under %s element, line %d"),
1016 // name.c_str(), m_iLineNumber);
1017 }
1018
1019 if (hostResourceBlockSize <= 0 && reservation > 0)
1020 {
1021 throw OVFLogicError(N_("Element HostResourceBlockSize is absent under %s element, line %d. "
1022 "see DMTF Schema Documentation %s"),
1023 name.c_str(), m_iLineNumber, DTMF_SPECS_URI);
1024 }
1025
1026 if (virtualResourceBlockSize <= 0 && virtualQuantity > 0)
1027 {
1028 throw OVFLogicError(N_("Element VirtualResourceBlockSize is absent under %s element, line %d. "
1029 "see DMTF Schema Documentation %s"),
1030 name.c_str(), m_iLineNumber, DTMF_SPECS_URI);
1031 }
1032
1033 if (virtualQuantity > 0 && strVirtualQuantityUnits.isEmpty())
1034 {
1035 throw OVFLogicError(N_("Element VirtualQuantityUnits is absent under %s element, line %d. "
1036 "see DMTF Schema Documentation %s"),
1037 name.c_str(), m_iLineNumber, DTMF_SPECS_URI);
1038 }
1039
1040 if (virtualResourceBlockSize <= 1 &&
1041 strVirtualQuantityUnits.compare(RTCString("count"), RTCString::CaseInsensitive) == 0
1042 )
1043 {
1044 throw OVFLogicError(N_("Element VirtualQuantityUnits is set to \"count\" "
1045 "while VirtualResourceBlockSize is set to 1. "
1046 "under %s element, line %d. "
1047 "It's needed to change on \"byte\". "
1048 "see DMTF Schema Documentation %s"),
1049 name.c_str(), m_iLineNumber, DTMF_SPECS_URI);
1050 }
1051}
1052
1053void EthernetPortItem::fillItem(const xml::ElementNode *item)
1054{
1055 VirtualHardwareItem::fillItem(item);
1056
1057 xml::NodesLoop loopItemChildren(*item);// all child elements
1058 const xml::ElementNode *pelmItemChild;
1059 while ((pelmItemChild = loopItemChildren.forAllNodes()))
1060 {
1061 }
1062}
1063
1064void EthernetPortItem::_checkConsistencyAndCompliance() RT_THROW(OVFLogicError)
1065{
1066 VirtualHardwareItem::_checkConsistencyAndCompliance();
1067}
1068
1069////////////////////////////////////////////////////////////////////////////////
1070//
1071// Errors
1072//
1073////////////////////////////////////////////////////////////////////////////////
1074
1075OVFLogicError::OVFLogicError(const char *aFormat, ...)
1076{
1077 char *pszNewMsg;
1078 va_list args;
1079 va_start(args, aFormat);
1080 RTStrAPrintfV(&pszNewMsg, aFormat, args);
1081 setWhat(pszNewMsg);
1082 RTStrFree(pszNewMsg);
1083 va_end(args);
1084}
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