VirtualBox

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

Last change on this file since 84347 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 48.2 KB
Line 
1/* $Id: ovfreader.cpp 82968 2020-02-04 10:35:17Z 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->ulLineNumber = 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 %u, previous at %u!\n",
472 m_strPath.c_str(), pItem->ulInstanceID, pItem->ulLineNumber, itDup->second->ulLineNumber));
473#else
474 throw OVFLogicError(N_("Error reading '%s': Duplicate ulInstanceID %u on line %u, previous at %u"),
475 m_strPath.c_str(), pItem->ulInstanceID,
476 pItem->ulLineNumber, itDup->second->ulLineNumber);
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.ulLineNumber);
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.ulLineNumber);
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
665 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)"),
666 m_strPath.c_str(),
667 ResourceType_OtherStorageDevice,
668 i.ulLineNumber, i.strResourceSubType.c_str() );
669 break;
670 }
671
672 case ResourceType_USBController: // 23
673 /* <Item ovf:required="false">
674 <rasd:Caption>usb</rasd:Caption>
675 <rasd:Description>USB Controller</rasd:Description>
676 <rasd:InstanceId>3</rasd:InstanceId>
677 <rasd:ResourceType>23</rasd:ResourceType>
678 <rasd:Address>0</rasd:Address>
679 <rasd:BusNumber>0</rasd:BusNumber>
680 </Item> */
681 vsys.fHasUsbController = true; // we have no additional information
682 break;
683
684 case ResourceType_SoundCard: // 35
685 /* <Item ovf:required="false">
686 <rasd:Caption>sound</rasd:Caption>
687 <rasd:Description>Sound Card</rasd:Description>
688 <rasd:InstanceId>10</rasd:InstanceId>
689 <rasd:ResourceType>35</rasd:ResourceType>
690 <rasd:ResourceSubType>ensoniq1371</rasd:ResourceSubType>
691 <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
692 <rasd:AddressOnParent>3</rasd:AddressOnParent>
693 </Item> */
694 vsys.strSoundCardType = i.strResourceSubType;
695 break;
696
697 default:
698 {
699 /* If this unknown resource type isn't required, we simply skip it. */
700 if (i.fResourceRequired)
701 {
702 throw OVFLogicError(N_("Error reading \"%s\": Unknown resource type %d in hardware item, line %d"),
703 m_strPath.c_str(),
704 i.resourceType,
705 i.ulLineNumber);
706 }
707 }
708 } // end switch
709 }
710
711 // now run through the items for a second time, but handle only
712 // hard disk images; otherwise the code would fail if a hard
713 // disk image appears in the OVF before its hard disk controller
714 for (itH = vsys.vecHardwareItems.begin(); itH != vsys.vecHardwareItems.end(); ++itH)
715 {
716 const VirtualHardwareItem &i = **itH;
717
718 // do some analysis
719 switch (i.resourceType)
720 {
721 case ResourceType_CDDrive: // 15
722 /* <Item ovf:required="false">
723 <rasd:Caption>cdrom1</rasd:Caption>
724 <rasd:InstanceId>7</rasd:InstanceId>
725 <rasd:ResourceType>15</rasd:ResourceType>
726 <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
727 <rasd:Parent>5</rasd:Parent>
728 <rasd:AddressOnParent>0</rasd:AddressOnParent>
729 </Item> */
730 case ResourceType_HardDisk: // 17
731 {
732 /* <Item>
733 <rasd:Caption>Harddisk 1</rasd:Caption>
734 <rasd:Description>HD</rasd:Description>
735 <rasd:ElementName>Hard Disk</rasd:ElementName>
736 <rasd:HostResource>ovf://disk/lamp</rasd:HostResource>
737 <rasd:InstanceID>5</rasd:InstanceID>
738 <rasd:Parent>4</rasd:Parent>
739 <rasd:ResourceType>17</rasd:ResourceType>
740 </Item> */
741
742 // look up the hard disk controller element whose InstanceID equals our Parent;
743 // this is how the connection is specified in OVF
744 ControllersMap::const_iterator it = vsys.mapControllers.find(i.ulParent);
745 if (it == vsys.mapControllers.end())
746 throw OVFLogicError(N_("Error reading \"%s\": Disk item with instance ID %d specifies invalid parent %d, line %d"),
747 m_strPath.c_str(),
748 i.ulInstanceID,
749 i.ulParent,
750 i.ulLineNumber);
751
752 VirtualDisk vd;
753 vd.idController = i.ulParent;
754 i.strAddressOnParent.toInt(vd.ulAddressOnParent);
755 // ovf://disk/lamp
756 // 123456789012345
757 if (i.strHostResource.startsWith("ovf://disk/"))
758 vd.strDiskId = i.strHostResource.substr(11);
759 else if (i.strHostResource.startsWith("ovf:/disk/"))
760 vd.strDiskId = i.strHostResource.substr(10);
761 else if (i.strHostResource.startsWith("/disk/"))
762 vd.strDiskId = i.strHostResource.substr(6);
763
764 //the error may be missed for CD, because CD can be empty
765 if ((vd.strDiskId.isEmpty() || (m_mapDisks.find(vd.strDiskId) == m_mapDisks.end()))
766 && i.resourceType == ResourceType_HardDisk)
767 throw OVFLogicError(N_("Error reading \"%s\": Disk item with instance ID %d specifies invalid host resource \"%s\", line %d"),
768 m_strPath.c_str(),
769 i.ulInstanceID,
770 i.strHostResource.c_str(),
771 i.ulLineNumber);
772
773 vsys.mapVirtualDisks[vd.strDiskId] = vd;
774 break;
775 }
776 default:
777 break;
778 }
779 }
780 }
781 else if ( !strcmp(pcszElemName, "OperatingSystemSection")
782 || !strcmp(pcszTypeAttr, "ovf:OperatingSystemSection_Type")
783 )
784 {
785 uint64_t cimos64;
786 if (!(pelmThis->getAttributeValue("id", cimos64)))
787 throw OVFLogicError(N_("Error reading \"%s\": missing or invalid 'ovf:id' attribute in operating system section element, line %d"),
788 m_strPath.c_str(),
789 pelmThis->getLineNumber());
790
791 vsys.cimos = (CIMOSType_T)cimos64;
792 const xml::ElementNode *pelmCIMOSDescription;
793 if ((pelmCIMOSDescription = pelmThis->findChildElement("Description")))
794 vsys.strCimosDesc = pelmCIMOSDescription->getValueN(RT_XML_CONTENT_SMALL);
795
796 const xml::ElementNode *pelmVBoxOSType;
797 if ((pelmVBoxOSType = pelmThis->findChildElementNS("vbox", // namespace
798 "OSType"))) // element name
799 vsys.strTypeVBox = pelmVBoxOSType->getValueN(RT_XML_CONTENT_SMALL);
800 }
801 else if ( (!strcmp(pcszElemName, "AnnotationSection"))
802 || (!strcmp(pcszTypeAttr, "ovf:AnnotationSection_Type"))
803 )
804 {
805 const xml::ElementNode *pelmAnnotation;
806 if ((pelmAnnotation = pelmThis->findChildElement("Annotation")))
807 vsys.strDescription = pelmAnnotation->getValueN(RT_XML_CONTENT_SMALL);
808 }
809 }
810}
811
812void VirtualHardwareItem::fillItem(const xml::ElementNode *item)
813{
814 xml::NodesLoop loopItemChildren(*item);// all child elements
815 const xml::ElementNode *pelmItemChild;
816 while ((pelmItemChild = loopItemChildren.forAllNodes()))
817 {
818 const char *pcszItemChildName = pelmItemChild->getName();
819 if (!strcmp(pcszItemChildName, "Description"))
820 strDescription = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
821 else if (!strcmp(pcszItemChildName, "Caption"))
822 strCaption = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
823 else if (!strcmp(pcszItemChildName, "ElementName"))
824 strElementName = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
825 else if ( !strcmp(pcszItemChildName, "InstanceID")
826 || !strcmp(pcszItemChildName, "InstanceId") )
827 pelmItemChild->copyValue(ulInstanceID);
828 else if (!strcmp(pcszItemChildName, "HostResource"))
829 strHostResource = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
830 else if (!strcmp(pcszItemChildName, "ResourceType"))
831 {
832 uint32_t ulType;
833 pelmItemChild->copyValue(ulType);
834 resourceType = (ResourceType_T)ulType;
835 fResourceRequired = true;
836 const char *pcszAttValue;
837 if (item->getAttributeValueN("required", pcszAttValue, RT_XML_ATTR_TINY))
838 {
839 if (!strcmp(pcszAttValue, "false"))
840 fResourceRequired = false;
841 }
842 }
843 else if (!strcmp(pcszItemChildName, "OtherResourceType"))
844 strOtherResourceType = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
845 else if (!strcmp(pcszItemChildName, "ResourceSubType"))
846 strResourceSubType = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
847 else if (!strcmp(pcszItemChildName, "AutomaticAllocation"))
848 fAutomaticAllocation = (!strcmp(pelmItemChild->getValueN(RT_XML_CONTENT_SMALL), "true")) ? true : false;
849 else if (!strcmp(pcszItemChildName, "AutomaticDeallocation"))
850 fAutomaticDeallocation = (!strcmp(pelmItemChild->getValueN(RT_XML_CONTENT_SMALL), "true")) ? true : false;
851 else if (!strcmp(pcszItemChildName, "Parent"))
852 pelmItemChild->copyValue(ulParent);
853 else if (!strcmp(pcszItemChildName, "Connection"))
854 strConnection = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
855 else if (!strcmp(pcszItemChildName, "Address"))
856 {
857 strAddress = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
858 pelmItemChild->copyValue(lAddress);
859 }
860 else if (!strcmp(pcszItemChildName, "AddressOnParent"))
861 strAddressOnParent = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
862 else if (!strcmp(pcszItemChildName, "AllocationUnits"))
863 strAllocationUnits = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
864 else if (!strcmp(pcszItemChildName, "VirtualQuantity"))
865 pelmItemChild->copyValue(ullVirtualQuantity);
866 else if (!strcmp(pcszItemChildName, "Reservation"))
867 pelmItemChild->copyValue(ullReservation);
868 else if (!strcmp(pcszItemChildName, "Limit"))
869 pelmItemChild->copyValue(ullLimit);
870 else if (!strcmp(pcszItemChildName, "Weight"))
871 pelmItemChild->copyValue(ullWeight);
872 else if (!strcmp(pcszItemChildName, "ConsumerVisibility"))
873 strConsumerVisibility = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
874 else if (!strcmp(pcszItemChildName, "MappingBehavior"))
875 strMappingBehavior = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
876 else if (!strcmp(pcszItemChildName, "PoolID"))
877 strPoolID = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
878 else if (!strcmp(pcszItemChildName, "BusNumber"))
879 pelmItemChild->copyValue(ulBusNumber);
880// else if (pelmItemChild->getPrefix() == NULL
881// || strcmp(pelmItemChild->getPrefix(), "vmw"))
882// throw OVFLogicError(N_("Unknown element \"%s\" under Item element, line %d"),
883// pcszItemChildName,
884// ulLineNumber);
885 }
886}
887
888void VirtualHardwareItem::_checkConsistencyAndCompliance() RT_THROW(OVFLogicError)
889{
890 RTCString name = getItemName();
891 if (resourceType == 0)
892 throw OVFLogicError(N_("Empty element ResourceType under %s element, line %d. see DMTF Schema Documentation %s"),
893 name.c_str(), ulLineNumber, DTMF_SPECS_URI);
894
895 /* Don't be too uptight about the ulInstanceID value. There are OVAs out
896 there which have ulInstanceID="%iid% for memory for instance, which is
897 no good reason for not being able to process them. bugref:8997 */
898 if (ulInstanceID == 0)
899 {
900 if ( resourceType == ResourceType_IDEController
901 || resourceType == ResourceType_OtherStorageDevice
902 || resourceType == ResourceType_ParallelSCSIHBA
903 || resourceType == ResourceType_iSCSIHBA //??
904 || resourceType == ResourceType_IBHCA ) //??
905 throw OVFLogicError(N_("Element InstanceID is absent under %s element, line %d. see DMTF Schema Documentation %s"),
906 name.c_str(), ulLineNumber, DTMF_SPECS_URI);
907 else
908 LogRel(("OVFREADER: Warning: Ignoring missing or invalid ulInstanceID under element %s, line %u\n",
909 name.c_str(), ulLineNumber));
910 }
911}
912
913void StorageItem::fillItem(const xml::ElementNode *item)
914{
915 VirtualHardwareItem::fillItem(item);
916
917 xml::NodesLoop loopItemChildren(*item);// all child elements
918 const xml::ElementNode *pelmItemChild;
919 while ((pelmItemChild = loopItemChildren.forAllNodes()))
920 {
921 const char *pcszItemChildName = pelmItemChild->getName();
922 if (!strcmp(pcszItemChildName, "HostExtentName"))
923 strHostExtentName = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
924 else if (!strcmp(pcszItemChildName, "OtherHostExtentNameFormat"))
925 strOtherHostExtentNameFormat = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
926 else if (!strcmp(pcszItemChildName, "OtherHostExtentNameNamespace"))
927 strOtherHostExtentNameNamespace = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
928 else if (!strcmp(pcszItemChildName, "VirtualQuantityUnits"))
929 strVirtualQuantityUnits = pelmItemChild->getValueN(RT_XML_CONTENT_SMALL);
930 else if (!strcmp(pcszItemChildName, "Access"))
931 {
932 uint32_t temp;
933 pelmItemChild->copyValue(temp);
934 accessType = (StorageAccessType_T)temp;
935 }
936 else if (!strcmp(pcszItemChildName, "HostExtentNameFormat"))
937 {
938 }
939 else if (!strcmp(pcszItemChildName, "HostExtentNameNamespace"))
940 {
941 }
942 else if (!strcmp(pcszItemChildName, "HostExtentStartingAddress"))
943 {
944 }
945 else if (!strcmp(pcszItemChildName, "HostResourceBlockSize"))
946 {
947 int64_t temp;
948 pelmItemChild->copyValue(temp);
949 hostResourceBlockSize = temp;
950 }
951 else if (!strcmp(pcszItemChildName, "Limit"))
952 {
953 int64_t temp;
954 pelmItemChild->copyValue(temp);
955 limit = temp;
956 }
957 else if (!strcmp(pcszItemChildName, "Reservation"))
958 {
959 int64_t temp;
960 pelmItemChild->copyValue(temp);
961 reservation = temp;
962 }
963 else if (!strcmp(pcszItemChildName, "VirtualQuantity"))
964 {
965 int64_t temp;
966 pelmItemChild->copyValue(temp);
967 virtualQuantity = temp;
968 }
969 else if (!strcmp(pcszItemChildName, "VirtualResourceBlockSize"))
970 {
971 int64_t temp;
972 pelmItemChild->copyValue(temp);
973 virtualResourceBlockSize = temp;
974 }
975 }
976}
977
978
979void StorageItem::_checkConsistencyAndCompliance() RT_THROW(OVFLogicError)
980{
981 VirtualHardwareItem::_checkConsistencyAndCompliance();
982
983 RTCString name = getItemName();
984
985 if (accessType == StorageAccessType_Unknown)
986 {
987 //throw OVFLogicError(N_("Access type is unknown under %s element, line %d"),
988 // name.c_str(), ulLineNumber);
989 }
990
991 if (hostResourceBlockSize <= 0 && reservation > 0)
992 {
993 throw OVFLogicError(N_("Element HostResourceBlockSize is absent under %s element, line %d. "
994 "see DMTF Schema Documentation %s"),
995 name.c_str(), ulLineNumber, DTMF_SPECS_URI);
996 }
997
998 if (virtualResourceBlockSize <= 0 && virtualQuantity > 0)
999 {
1000 throw OVFLogicError(N_("Element VirtualResourceBlockSize is absent under %s element, line %d. "
1001 "see DMTF Schema Documentation %s"),
1002 name.c_str(), ulLineNumber, DTMF_SPECS_URI);
1003 }
1004
1005 if (virtualQuantity > 0 && strVirtualQuantityUnits.isEmpty())
1006 {
1007 throw OVFLogicError(N_("Element VirtualQuantityUnits is absent under %s element, line %d. "
1008 "see DMTF Schema Documentation %s"),
1009 name.c_str(), ulLineNumber, DTMF_SPECS_URI);
1010 }
1011
1012 if (virtualResourceBlockSize <= 1 &&
1013 strVirtualQuantityUnits.compare(RTCString("count"), RTCString::CaseInsensitive) == 0
1014 )
1015 {
1016 throw OVFLogicError(N_("Element VirtualQuantityUnits is set to \"count\" "
1017 "while VirtualResourceBlockSize is set to 1. "
1018 "under %s element, line %d. "
1019 "It's needed to change on \"byte\". "
1020 "see DMTF Schema Documentation %s"),
1021 name.c_str(), ulLineNumber, DTMF_SPECS_URI);
1022 }
1023}
1024
1025void EthernetPortItem::fillItem(const xml::ElementNode *item)
1026{
1027 VirtualHardwareItem::fillItem(item);
1028
1029 xml::NodesLoop loopItemChildren(*item);// all child elements
1030 const xml::ElementNode *pelmItemChild;
1031 while ((pelmItemChild = loopItemChildren.forAllNodes()))
1032 {
1033 }
1034}
1035
1036void EthernetPortItem::_checkConsistencyAndCompliance() RT_THROW(OVFLogicError)
1037{
1038 VirtualHardwareItem::_checkConsistencyAndCompliance();
1039}
1040
1041////////////////////////////////////////////////////////////////////////////////
1042//
1043// Errors
1044//
1045////////////////////////////////////////////////////////////////////////////////
1046
1047OVFLogicError::OVFLogicError(const char *aFormat, ...)
1048{
1049 char *pszNewMsg;
1050 va_list args;
1051 va_start(args, aFormat);
1052 RTStrAPrintfV(&pszNewMsg, aFormat, args);
1053 setWhat(pszNewMsg);
1054 RTStrFree(pszNewMsg);
1055 va_end(args);
1056}
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