VirtualBox

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

Last change on this file since 86692 was 85222, checked in by vboxsync, 4 years ago

Main/ovfreader: Clang 11 complains about a bunch of unused members, so I've #if'ed them out. Also a signedness issue with line numbers. Cleaned up some modernistic/random continuation indentation. bugref:9790

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