VirtualBox

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

Last change on this file since 31731 was 29893, checked in by vboxsync, 15 years ago

OVFReader: fix another type lookup regression on OVF 0.9 import

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