VirtualBox

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

Last change on this file since 98103 was 98103, checked in by vboxsync, 23 months ago

Copyright year updates by scm.

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