VirtualBox

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

Last change on this file since 33517 was 32565, checked in by vboxsync, 14 years ago

Runtime/Main: add xml/ovf parsing in memory

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