VirtualBox

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

Last change on this file since 46141 was 46140, checked in by vboxsync, 12 years ago

issue 5429. Support CD/DVD images attached to IDE/SATA during OVF import/export.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette