VirtualBox

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

Last change on this file since 77744 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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