VirtualBox

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

Last change on this file since 42696 was 37869, checked in by vboxsync, 13 years ago

Main: some mis-indented breaks

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