VirtualBox

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

Last change on this file since 59553 was 59553, checked in by vboxsync, 9 years ago

ovfreader.cpp: Adjusted AHCI acceptance weirdness so it can handle tdAppliance1-t4.ova and left a @todo on a weird 'Caption' check.

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