VirtualBox

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

Last change on this file since 29853 was 29422, checked in by vboxsync, 15 years ago

Main/OVF: fix IDE channel confusion introduced with earlier OVF two-channel IDE fix (trunk regression)

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