VirtualBox

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

Last change on this file since 67954 was 65088, checked in by vboxsync, 8 years ago

Main: doxygen fixes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 46.6 KB
Line 
1/* $Id: ovfreader.cpp 65088 2017-01-03 20:52:49Z 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-2016 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 pReferencesElem "References" element from OVF, for looking up file specifications;
123 * 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 * @return
298 */
299void OVFReader::HandleNetworkSection(const xml::ElementNode * /* pSectionElem */)
300{
301 // we ignore network sections for now
302
303// xml::NodesLoop loopNetworks(*pSectionElem, "Network");
304// const xml::Node *pelmNetwork;
305// while ((pelmNetwork = loopNetworks.forAllNodes()))
306// {
307// Network n;
308// if (!(pelmNetwork->getAttributeValue("name", n.strNetworkName)))
309// return setError(VBOX_E_FILE_ERROR,
310// tr("Error reading \"%s\": missing 'name' attribute in 'Network', line %d"),
311// pcszPath,
312// pelmNetwork->getLineNumber());
313//
314// m->mapNetworks[n.strNetworkName] = n;
315// }
316}
317
318/**
319 * Private helper method that handles a "VirtualSystem" element in the OVF XML.
320 * Gets called indirectly from IAppliance::read().
321 *
322 * @param pelmVirtualSystem
323 * @return
324 */
325void OVFReader::HandleVirtualSystemContent(const xml::ElementNode *pelmVirtualSystem)
326{
327 VirtualSystem vsys;
328
329 // peek under the <VirtualSystem> node whether we have a <vbox:Machine> node;
330 // that case case, the caller can completely ignore the OVF but only load the VBox machine XML
331 vsys.pelmVBoxMachine = pelmVirtualSystem->findChildElementNS("vbox", "Machine");
332
333 // now look for real OVF
334 const xml::AttributeNode *pIdAttr = pelmVirtualSystem->findAttribute("id");
335 if (pIdAttr)
336 vsys.strName = pIdAttr->getValue();
337
338 xml::NodesLoop loop(*pelmVirtualSystem); // all child elements
339 const xml::ElementNode *pelmThis;
340 while ((pelmThis = loop.forAllNodes()))
341 {
342 const char *pcszElemName = pelmThis->getName();
343 const char *pcszTypeAttr = "";
344 if (!strcmp(pcszElemName, "Section")) // OVF 0.9 used "Section" element always with a varying "type" attribute
345 {
346 const xml::AttributeNode *pTypeAttr = pelmThis->findAttribute("type");
347 if (pTypeAttr)
348 pcszTypeAttr = pTypeAttr->getValue();
349 else
350 throw OVFLogicError(N_("Error reading \"%s\": element \"Section\" has no \"type\" attribute, line %d"),
351 m_strPath.c_str(),
352 pelmThis->getLineNumber());
353 }
354
355 if ( !strcmp(pcszElemName, "EulaSection")
356 || !strcmp(pcszTypeAttr, "ovf:EulaSection_Type")
357 )
358 {
359 /* <EulaSection>
360 <Info ovf:msgid="6">License agreement for the Virtual System.</Info>
361 <License ovf:msgid="1">License terms can go in here.</License>
362 </EulaSection> */
363
364 const xml::ElementNode *pelmLicense;
365 if ((pelmLicense = pelmThis->findChildElement("License")))
366 vsys.strLicenseText = pelmLicense->getValue();
367 }
368 if ( !strcmp(pcszElemName, "ProductSection")
369 || !strcmp(pcszTypeAttr, "ovf:ProductSection_Type")
370 )
371 {
372 /* <Section ovf:required="false" xsi:type="ovf:ProductSection_Type">
373 <Info>Meta-information about the installed software</Info>
374 <Product>VAtest</Product>
375 <Vendor>SUN Microsystems</Vendor>
376 <Version>10.0</Version>
377 <ProductUrl>http://blogs.sun.com/VirtualGuru</ProductUrl>
378 <VendorUrl>http://www.sun.com</VendorUrl>
379 </Section> */
380 const xml::ElementNode *pelmProduct;
381 if ((pelmProduct = pelmThis->findChildElement("Product")))
382 vsys.strProduct = pelmProduct->getValue();
383 const xml::ElementNode *pelmVendor;
384 if ((pelmVendor = pelmThis->findChildElement("Vendor")))
385 vsys.strVendor = pelmVendor->getValue();
386 const xml::ElementNode *pelmVersion;
387 if ((pelmVersion = pelmThis->findChildElement("Version")))
388 vsys.strVersion = pelmVersion->getValue();
389 const xml::ElementNode *pelmProductUrl;
390 if ((pelmProductUrl = pelmThis->findChildElement("ProductUrl")))
391 vsys.strProductUrl = pelmProductUrl->getValue();
392 const xml::ElementNode *pelmVendorUrl;
393 if ((pelmVendorUrl = pelmThis->findChildElement("VendorUrl")))
394 vsys.strVendorUrl = pelmVendorUrl->getValue();
395 }
396 else if ( !strcmp(pcszElemName, "VirtualHardwareSection")
397 || !strcmp(pcszTypeAttr, "ovf:VirtualHardwareSection_Type")
398 )
399 {
400 const xml::ElementNode *pelmSystem, *pelmVirtualSystemType;
401 if ((pelmSystem = pelmThis->findChildElement("System")))
402 {
403 /* <System>
404 <vssd:Description>Description of the virtual hardware section.</vssd:Description>
405 <vssd:ElementName>vmware</vssd:ElementName>
406 <vssd:InstanceID>1</vssd:InstanceID>
407 <vssd:VirtualSystemIdentifier>MyLampService</vssd:VirtualSystemIdentifier>
408 <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType>
409 </System>*/
410 if ((pelmVirtualSystemType = pelmSystem->findChildElement("VirtualSystemType")))
411 vsys.strVirtualSystemType = pelmVirtualSystemType->getValue();
412 }
413
414 {
415 xml::NodesLoop loopVirtualHardwareItems(*pelmThis, "Item"); // all "Item" child elements
416 const xml::ElementNode *pelmItem;
417 while ((pelmItem = loopVirtualHardwareItems.forAllNodes()))
418 {
419 VirtualHardwareItem i;
420
421 i.ulLineNumber = pelmItem->getLineNumber();
422 i.fillItem(pelmItem);
423 try{
424 i.checkConsistencyAndCompliance();
425 }
426 catch (OVFLogicError &e)
427 {
428 throw OVFLogicError(N_("Error reading \"%s\": \"%s\""),
429 m_strPath.c_str(),
430 e.what());
431 }
432
433 // store!
434 vsys.mapHardwareItems[i.ulInstanceID] = i;
435 }
436 }
437
438 {
439 xml::NodesLoop loopVirtualHardwareItems(*pelmThis, "StorageItem");// all "StorageItem" child elements
440 const xml::ElementNode *pelmItem;
441 while ((pelmItem = loopVirtualHardwareItems.forAllNodes()))
442 {
443 StorageItem i;
444
445 i.ulLineNumber = pelmItem->getLineNumber();
446 i.fillItem(pelmItem);
447
448 try
449 {
450 i.checkConsistencyAndCompliance();
451 }
452 catch (OVFLogicError &e)
453 {
454 throw OVFLogicError(N_("Error reading \"%s\": \"%s\""),
455 m_strPath.c_str(),
456 e.what());
457 }
458
459 vsys.mapHardwareItems[i.ulInstanceID] = i;
460 }
461 }
462
463 {
464 xml::NodesLoop loopVirtualHardwareItems(*pelmThis, "EthernetPortItem");// all "EthernetPortItem" child elements
465 const xml::ElementNode *pelmItem;
466 while ((pelmItem = loopVirtualHardwareItems.forAllNodes()))
467 {
468 EthernetPortItem i;
469
470 i.ulLineNumber = pelmItem->getLineNumber();
471 i.fillItem(pelmItem);
472
473 try{
474 i.checkConsistencyAndCompliance();
475 }
476 catch (OVFLogicError &e)
477 {
478 throw OVFLogicError(N_("Error reading \"%s\": \"%s\""),
479 m_strPath.c_str(),
480 e.what());
481 }
482
483 vsys.mapHardwareItems[i.ulInstanceID] = i;
484 }
485 }
486
487 HardDiskController *pPrimaryIDEController = NULL;// will be set once found
488
489 // now go thru all hardware items and handle them according to their type;
490 // in this first loop we handle all items _except_ hard disk images,
491 // which we'll handle in a second loop below
492 HardwareItemsMap::const_iterator itH;
493 for (itH = vsys.mapHardwareItems.begin();
494 itH != vsys.mapHardwareItems.end();
495 ++itH)
496 {
497 const VirtualHardwareItem &i = itH->second;
498
499 // do some analysis
500 switch (i.resourceType)
501 {
502 case ResourceType_Processor: // 3
503 /* <rasd:Caption>1 virtual CPU</rasd:Caption>
504 <rasd:Description>Number of virtual CPUs</rasd:Description>
505 <rasd:ElementName>virtual CPU</rasd:ElementName>
506 <rasd:InstanceID>1</rasd:InstanceID>
507 <rasd:ResourceType>3</rasd:ResourceType>
508 <rasd:VirtualQuantity>1</rasd:VirtualQuantity>*/
509 if (i.ullVirtualQuantity < UINT16_MAX)
510 vsys.cCPUs = (uint16_t)i.ullVirtualQuantity;
511 else
512 throw OVFLogicError(N_("Error reading \"%s\": CPU count %RI64 is larger than %d, line %d"),
513 m_strPath.c_str(),
514 i.ullVirtualQuantity,
515 UINT16_MAX,
516 i.ulLineNumber);
517 break;
518
519 case ResourceType_Memory: // 4
520 if ( i.strAllocationUnits == "MegaBytes" // found in OVF created by OVF toolkit
521 || i.strAllocationUnits == "MB" // found in MS docs
522 || i.strAllocationUnits == "byte * 2^20" // suggested by OVF spec DSP0243 page 21
523 )
524 vsys.ullMemorySize = i.ullVirtualQuantity * _1M;
525 else
526 throw OVFLogicError(N_("Error reading \"%s\": Invalid allocation unit \"%s\" specified with memory size item, line %d"),
527 m_strPath.c_str(),
528 i.strAllocationUnits.c_str(),
529 i.ulLineNumber);
530 break;
531
532 case ResourceType_IDEController: // 5
533 {
534 /* <Item>
535 <rasd:Caption>ideController0</rasd:Caption>
536 <rasd:Description>IDE Controller</rasd:Description>
537 <rasd:InstanceId>5</rasd:InstanceId>
538 <rasd:ResourceType>5</rasd:ResourceType>
539 <rasd:Address>0</rasd:Address>
540 <rasd:BusNumber>0</rasd:BusNumber>
541 </Item> */
542 HardDiskController hdc;
543 hdc.system = HardDiskController::IDE;
544 hdc.idController = i.ulInstanceID;
545 hdc.strControllerType = i.strResourceSubType;
546
547 hdc.lAddress = i.lAddress;
548
549 if (!pPrimaryIDEController)
550 // this is the first IDE controller found: then mark it as "primary"
551 hdc.fPrimary = true;
552 else
553 {
554 // this is the second IDE controller found: If VMware exports two
555 // IDE controllers, it seems that they are given an "Address" of 0
556 // an 1, respectively, so assume address=0 means primary controller
557 if ( pPrimaryIDEController->lAddress == 0
558 && hdc.lAddress == 1
559 )
560 {
561 pPrimaryIDEController->fPrimary = true;
562 hdc.fPrimary = false;
563 }
564 else if ( pPrimaryIDEController->lAddress == 1
565 && hdc.lAddress == 0
566 )
567 {
568 pPrimaryIDEController->fPrimary = false;
569 hdc.fPrimary = false;
570 }
571 else
572 // then we really can't tell, just hope for the best
573 hdc.fPrimary = false;
574 }
575
576 vsys.mapControllers[i.ulInstanceID] = hdc;
577 if (!pPrimaryIDEController)
578 pPrimaryIDEController = &vsys.mapControllers[i.ulInstanceID];
579 break;
580 }
581
582 case ResourceType_ParallelSCSIHBA: // 6 SCSI controller
583 {
584 /* <Item>
585 <rasd:Caption>SCSI Controller 0 - LSI Logic</rasd:Caption>
586 <rasd:Description>SCI Controller</rasd:Description>
587 <rasd:ElementName>SCSI controller</rasd:ElementName>
588 <rasd:InstanceID>4</rasd:InstanceID>
589 <rasd:ResourceSubType>LsiLogic</rasd:ResourceSubType>
590 <rasd:ResourceType>6</rasd:ResourceType>
591 </Item> */
592 HardDiskController hdc;
593 hdc.system = HardDiskController::SCSI;
594 hdc.idController = i.ulInstanceID;
595 hdc.strControllerType = i.strResourceSubType;
596
597 vsys.mapControllers[i.ulInstanceID] = hdc;
598 break;
599 }
600
601 case ResourceType_EthernetAdapter: // 10
602 {
603 /* <Item>
604 <rasd:Caption>Ethernet adapter on 'Bridged'</rasd:Caption>
605 <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
606 <rasd:Connection>Bridged</rasd:Connection>
607 <rasd:InstanceID>6</rasd:InstanceID>
608 <rasd:ResourceType>10</rasd:ResourceType>
609 <rasd:ResourceSubType>E1000</rasd:ResourceSubType>
610 </Item>
611
612 OVF spec DSP 0243 page 21:
613 "For an Ethernet adapter, this specifies the abstract network connection name
614 for the virtual machine. All Ethernet adapters that specify the same abstract
615 network connection name within an OVF package shall be deployed on the same
616 network. The abstract network connection name shall be listed in the NetworkSection
617 at the outermost envelope level." */
618
619 // only store the name
620 EthernetAdapter ea;
621 ea.strAdapterType = i.strResourceSubType;
622 ea.strNetworkName = i.strConnection;
623 vsys.llEthernetAdapters.push_back(ea);
624 break;
625 }
626
627 case ResourceType_FloppyDrive: // 14
628 vsys.fHasFloppyDrive = true; // we have no additional information
629 break;
630
631 case ResourceType_CDDrive: // 15
632 /* <Item ovf:required="false">
633 <rasd:Caption>cdrom1</rasd:Caption>
634 <rasd:InstanceId>7</rasd:InstanceId>
635 <rasd:ResourceType>15</rasd:ResourceType>
636 <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
637 <rasd:Parent>5</rasd:Parent>
638 <rasd:AddressOnParent>0</rasd:AddressOnParent>
639 </Item> */
640 // I tried to see what happens if I set an ISO for the CD-ROM in VMware Workstation,
641 // but then the ovftool dies with "Device backing not supported". So I guess if
642 // VMware can't export ISOs, then we don't need to be able to import them right now.
643 vsys.fHasCdromDrive = true; // we have no additional information
644 break;
645
646 case ResourceType_HardDisk: // 17
647 // handled separately in second loop below
648 break;
649
650 case ResourceType_OtherStorageDevice: // 20 SATA controller
651 {
652 /* <Item>
653 <rasd:Description>SATA Controller</rasd:Description>
654 <rasd:Caption>sataController0</rasd:Caption>
655 <rasd:InstanceID>4</rasd:InstanceID>
656 <rasd:ResourceType>20</rasd:ResourceType>
657 <rasd:ResourceSubType>AHCI</rasd:ResourceSubType>
658 <rasd:Address>0</rasd:Address>
659 <rasd:BusNumber>0</rasd:BusNumber>
660 </Item> */
661 if ( i.strResourceSubType.compare("AHCI", RTCString::CaseInsensitive) == 0
662 || i.strResourceSubType.compare("vmware.sata.ahci", RTCString::CaseInsensitive) == 0)
663 {
664 HardDiskController hdc;
665 hdc.system = HardDiskController::SATA;
666 hdc.idController = i.ulInstanceID;
667 hdc.strControllerType = i.strResourceSubType;
668
669 vsys.mapControllers[i.ulInstanceID] = hdc;
670 }
671 else
672 throw OVFLogicError(N_("Error reading \"%s\": Host resource of type \"Other Storage Device (%d)\" is supported with SATA AHCI controllers only, line %d (subtype:%s)"),
673 m_strPath.c_str(),
674 ResourceType_OtherStorageDevice,
675 i.ulLineNumber, i.strResourceSubType.c_str() );
676 break;
677 }
678
679 case ResourceType_USBController: // 23
680 /* <Item ovf:required="false">
681 <rasd:Caption>usb</rasd:Caption>
682 <rasd:Description>USB Controller</rasd:Description>
683 <rasd:InstanceId>3</rasd:InstanceId>
684 <rasd:ResourceType>23</rasd:ResourceType>
685 <rasd:Address>0</rasd:Address>
686 <rasd:BusNumber>0</rasd:BusNumber>
687 </Item> */
688 vsys.fHasUsbController = true; // we have no additional information
689 break;
690
691 case ResourceType_SoundCard: // 35
692 /* <Item ovf:required="false">
693 <rasd:Caption>sound</rasd:Caption>
694 <rasd:Description>Sound Card</rasd:Description>
695 <rasd:InstanceId>10</rasd:InstanceId>
696 <rasd:ResourceType>35</rasd:ResourceType>
697 <rasd:ResourceSubType>ensoniq1371</rasd:ResourceSubType>
698 <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
699 <rasd:AddressOnParent>3</rasd:AddressOnParent>
700 </Item> */
701 vsys.strSoundCardType = i.strResourceSubType;
702 break;
703
704 default:
705 {
706 /* If this unknown resource type isn't required, we simply skip it. */
707 if (i.fResourceRequired)
708 {
709 throw OVFLogicError(N_("Error reading \"%s\": Unknown resource type %d in hardware item, line %d"),
710 m_strPath.c_str(),
711 i.resourceType,
712 i.ulLineNumber);
713 }
714 }
715 } // end switch
716 }
717
718 // now run through the items for a second time, but handle only
719 // hard disk images; otherwise the code would fail if a hard
720 // disk image appears in the OVF before its hard disk controller
721 for (itH = vsys.mapHardwareItems.begin();
722 itH != vsys.mapHardwareItems.end();
723 ++itH)
724 {
725 const VirtualHardwareItem &i = itH->second;
726
727 // do some analysis
728 switch (i.resourceType)
729 {
730 case ResourceType_CDDrive: // 15
731 /* <Item ovf:required="false">
732 <rasd:Caption>cdrom1</rasd:Caption>
733 <rasd:InstanceId>7</rasd:InstanceId>
734 <rasd:ResourceType>15</rasd:ResourceType>
735 <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
736 <rasd:Parent>5</rasd:Parent>
737 <rasd:AddressOnParent>0</rasd:AddressOnParent>
738 </Item> */
739 case ResourceType_HardDisk: // 17
740 {
741 /* <Item>
742 <rasd:Caption>Harddisk 1</rasd:Caption>
743 <rasd:Description>HD</rasd:Description>
744 <rasd:ElementName>Hard Disk</rasd:ElementName>
745 <rasd:HostResource>ovf://disk/lamp</rasd:HostResource>
746 <rasd:InstanceID>5</rasd:InstanceID>
747 <rasd:Parent>4</rasd:Parent>
748 <rasd:ResourceType>17</rasd:ResourceType>
749 </Item> */
750
751 // look up the hard disk controller element whose InstanceID equals our Parent;
752 // this is how the connection is specified in OVF
753 ControllersMap::const_iterator it = vsys.mapControllers.find(i.ulParent);
754 if (it == vsys.mapControllers.end())
755 throw OVFLogicError(N_("Error reading \"%s\": Disk item with instance ID %d specifies invalid parent %d, line %d"),
756 m_strPath.c_str(),
757 i.ulInstanceID,
758 i.ulParent,
759 i.ulLineNumber);
760
761 VirtualDisk vd;
762 vd.idController = i.ulParent;
763 i.strAddressOnParent.toInt(vd.ulAddressOnParent);
764 // ovf://disk/lamp
765 // 123456789012345
766 if (i.strHostResource.startsWith("ovf://disk/"))
767 vd.strDiskId = i.strHostResource.substr(11);
768 else if (i.strHostResource.startsWith("ovf:/disk/"))
769 vd.strDiskId = i.strHostResource.substr(10);
770 else if (i.strHostResource.startsWith("/disk/"))
771 vd.strDiskId = i.strHostResource.substr(6);
772
773 //the error may be missed for CD, because CD can be empty
774 if ((vd.strDiskId.isEmpty() || (m_mapDisks.find(vd.strDiskId) == m_mapDisks.end()))
775 && i.resourceType == ResourceType_HardDisk)
776 throw OVFLogicError(N_("Error reading \"%s\": Disk item with instance ID %d specifies invalid host resource \"%s\", line %d"),
777 m_strPath.c_str(),
778 i.ulInstanceID,
779 i.strHostResource.c_str(),
780 i.ulLineNumber);
781
782 vsys.mapVirtualDisks[vd.strDiskId] = vd;
783 break;
784 }
785 default:
786 break;
787 }
788 }
789 }
790 else if ( !strcmp(pcszElemName, "OperatingSystemSection")
791 || !strcmp(pcszTypeAttr, "ovf:OperatingSystemSection_Type")
792 )
793 {
794 uint64_t cimos64;
795 if (!(pelmThis->getAttributeValue("id", cimos64)))
796 throw OVFLogicError(N_("Error reading \"%s\": missing or invalid 'ovf:id' attribute in operating system section element, line %d"),
797 m_strPath.c_str(),
798 pelmThis->getLineNumber());
799
800 vsys.cimos = (CIMOSType_T)cimos64;
801 const xml::ElementNode *pelmCIMOSDescription;
802 if ((pelmCIMOSDescription = pelmThis->findChildElement("Description")))
803 vsys.strCimosDesc = pelmCIMOSDescription->getValue();
804
805 const xml::ElementNode *pelmVBoxOSType;
806 if ((pelmVBoxOSType = pelmThis->findChildElementNS("vbox", // namespace
807 "OSType"))) // element name
808 vsys.strTypeVBox = pelmVBoxOSType->getValue();
809 }
810 else if ( (!strcmp(pcszElemName, "AnnotationSection"))
811 || (!strcmp(pcszTypeAttr, "ovf:AnnotationSection_Type"))
812 )
813 {
814 const xml::ElementNode *pelmAnnotation;
815 if ((pelmAnnotation = pelmThis->findChildElement("Annotation")))
816 vsys.strDescription = pelmAnnotation->getValue();
817 }
818 }
819
820 // now create the virtual system
821 m_llVirtualSystems.push_back(vsys);
822}
823
824void VirtualHardwareItem::fillItem(const xml::ElementNode *item)
825{
826 xml::NodesLoop loopItemChildren(*item);// all child elements
827 const xml::ElementNode *pelmItemChild;
828 while ((pelmItemChild = loopItemChildren.forAllNodes()))
829 {
830 const char *pcszItemChildName = pelmItemChild->getName();
831 if (!strcmp(pcszItemChildName, "Description"))
832 strDescription = pelmItemChild->getValue();
833 else if (!strcmp(pcszItemChildName, "Caption"))
834 strCaption = pelmItemChild->getValue();
835 else if (!strcmp(pcszItemChildName, "ElementName"))
836 strElementName = pelmItemChild->getValue();
837 else if ((!strcmp(pcszItemChildName, "InstanceID"))
838 ||(!strcmp(pcszItemChildName, "InstanceId"))
839 )
840 pelmItemChild->copyValue(ulInstanceID);
841 else if (!strcmp(pcszItemChildName, "HostResource"))
842 strHostResource = pelmItemChild->getValue();
843 else if (!strcmp(pcszItemChildName, "ResourceType"))
844 {
845 uint32_t ulType;
846 pelmItemChild->copyValue(ulType);
847 resourceType = (ResourceType_T)ulType;
848 fResourceRequired = true;
849 const char *pcszAttValue;
850 if (item->getAttributeValue("required", pcszAttValue))
851 {
852 if (!strcmp(pcszAttValue, "false"))
853 fResourceRequired = false;
854 }
855 }
856 else if (!strcmp(pcszItemChildName, "OtherResourceType"))
857 strOtherResourceType = pelmItemChild->getValue();
858 else if (!strcmp(pcszItemChildName, "ResourceSubType"))
859 strResourceSubType = pelmItemChild->getValue();
860 else if (!strcmp(pcszItemChildName, "AutomaticAllocation"))
861 fAutomaticAllocation = (!strcmp(pelmItemChild->getValue(), "true")) ? true : false;
862 else if (!strcmp(pcszItemChildName, "AutomaticDeallocation"))
863 fAutomaticDeallocation = (!strcmp(pelmItemChild->getValue(), "true")) ? true : false;
864 else if (!strcmp(pcszItemChildName, "Parent"))
865 pelmItemChild->copyValue(ulParent);
866 else if (!strcmp(pcszItemChildName, "Connection"))
867 strConnection = pelmItemChild->getValue();
868 else if (!strcmp(pcszItemChildName, "Address"))
869 {
870 strAddress = pelmItemChild->getValue();
871 pelmItemChild->copyValue(lAddress);
872 }
873 else if (!strcmp(pcszItemChildName, "AddressOnParent"))
874 strAddressOnParent = pelmItemChild->getValue();
875 else if (!strcmp(pcszItemChildName, "AllocationUnits"))
876 strAllocationUnits = pelmItemChild->getValue();
877 else if (!strcmp(pcszItemChildName, "VirtualQuantity"))
878 pelmItemChild->copyValue(ullVirtualQuantity);
879 else if (!strcmp(pcszItemChildName, "Reservation"))
880 pelmItemChild->copyValue(ullReservation);
881 else if (!strcmp(pcszItemChildName, "Limit"))
882 pelmItemChild->copyValue(ullLimit);
883 else if (!strcmp(pcszItemChildName, "Weight"))
884 pelmItemChild->copyValue(ullWeight);
885 else if (!strcmp(pcszItemChildName, "ConsumerVisibility"))
886 strConsumerVisibility = pelmItemChild->getValue();
887 else if (!strcmp(pcszItemChildName, "MappingBehavior"))
888 strMappingBehavior = pelmItemChild->getValue();
889 else if (!strcmp(pcszItemChildName, "PoolID"))
890 strPoolID = pelmItemChild->getValue();
891 else if (!strcmp(pcszItemChildName, "BusNumber"))
892 pelmItemChild->copyValue(ulBusNumber);
893// else if (pelmItemChild->getPrefix() == NULL
894// || strcmp(pelmItemChild->getPrefix(), "vmw"))
895// throw OVFLogicError(N_("Unknown element \"%s\" under Item element, line %d"),
896// pcszItemChildName,
897// ulLineNumber);
898 }
899}
900
901void VirtualHardwareItem::_checkConsistencyAndCompliance() RT_THROW(OVFLogicError)
902{
903 RTCString name = getItemName();
904 if (ulInstanceID == 0)
905 throw OVFLogicError(N_("Element InstanceID is absent under %s element, line %d. "
906 "see DMTF Schema Documentation %s"),
907 name.c_str(), ulLineNumber, DTMF_SPECS_URI);
908 if (resourceType == 0)
909 throw OVFLogicError(N_("Empty element ResourceType under %s element, line %d. "
910 "see DMTF Schema Documentation %s"),
911 name.c_str(), ulLineNumber, DTMF_SPECS_URI);
912}
913
914void StorageItem::fillItem(const xml::ElementNode *item)
915{
916 VirtualHardwareItem::fillItem(item);
917
918 xml::NodesLoop loopItemChildren(*item);// all child elements
919 const xml::ElementNode *pelmItemChild;
920 while ((pelmItemChild = loopItemChildren.forAllNodes()))
921 {
922 const char *pcszItemChildName = pelmItemChild->getName();
923 if (!strcmp(pcszItemChildName, "HostExtentName"))
924 strHostExtentName = pelmItemChild->getValue();
925 else if (!strcmp(pcszItemChildName, "OtherHostExtentNameFormat"))
926 strOtherHostExtentNameFormat = pelmItemChild->getValue();
927 else if (!strcmp(pcszItemChildName, "OtherHostExtentNameNamespace"))
928 strOtherHostExtentNameNamespace = pelmItemChild->getValue();
929 else if (!strcmp(pcszItemChildName, "VirtualQuantityUnits"))
930 strVirtualQuantityUnits = pelmItemChild->getValue();
931 else if (!strcmp(pcszItemChildName, "Access"))
932 {
933 uint32_t temp;
934 pelmItemChild->copyValue(temp);
935 accessType = (StorageAccessType_T)temp;
936 }
937 else if (!strcmp(pcszItemChildName, "HostExtentNameFormat"))
938 {
939 }
940 else if (!strcmp(pcszItemChildName, "HostExtentNameNamespace"))
941 {
942 }
943 else if (!strcmp(pcszItemChildName, "HostExtentStartingAddress"))
944 {
945 }
946 else if (!strcmp(pcszItemChildName, "HostResourceBlockSize"))
947 {
948 int64_t temp;
949 pelmItemChild->copyValue(temp);
950 hostResourceBlockSize = temp;
951 }
952 else if (!strcmp(pcszItemChildName, "Limit"))
953 {
954 int64_t temp;
955 pelmItemChild->copyValue(temp);
956 limit = temp;
957 }
958 else if (!strcmp(pcszItemChildName, "Reservation"))
959 {
960 int64_t temp;
961 pelmItemChild->copyValue(temp);
962 reservation = temp;
963 }
964 else if (!strcmp(pcszItemChildName, "VirtualQuantity"))
965 {
966 int64_t temp;
967 pelmItemChild->copyValue(temp);
968 virtualQuantity = temp;
969 }
970 else if (!strcmp(pcszItemChildName, "VirtualResourceBlockSize"))
971 {
972 int64_t temp;
973 pelmItemChild->copyValue(temp);
974 virtualResourceBlockSize = temp;
975 }
976 }
977}
978
979
980void StorageItem::_checkConsistencyAndCompliance() RT_THROW(OVFLogicError)
981{
982 VirtualHardwareItem::_checkConsistencyAndCompliance();
983
984 RTCString name = getItemName();
985
986 if (accessType == StorageAccessType_Unknown)
987 {
988 //throw OVFLogicError(N_("Access type is unknown under %s element, line %d"),
989 // name.c_str(), ulLineNumber);
990 }
991
992 if (hostResourceBlockSize <= 0 && reservation > 0)
993 {
994 throw OVFLogicError(N_("Element HostResourceBlockSize is absent under %s element, line %d. "
995 "see DMTF Schema Documentation %s"),
996 name.c_str(), ulLineNumber, DTMF_SPECS_URI);
997 }
998
999 if (virtualResourceBlockSize <= 0 && virtualQuantity > 0)
1000 {
1001 throw OVFLogicError(N_("Element VirtualResourceBlockSize is absent under %s element, line %d. "
1002 "see DMTF Schema Documentation %s"),
1003 name.c_str(), ulLineNumber, DTMF_SPECS_URI);
1004 }
1005
1006 if (virtualQuantity > 0 && strVirtualQuantityUnits.isEmpty())
1007 {
1008 throw OVFLogicError(N_("Element VirtualQuantityUnits is absent under %s element, line %d. "
1009 "see DMTF Schema Documentation %s"),
1010 name.c_str(), ulLineNumber, DTMF_SPECS_URI);
1011 }
1012
1013 if (virtualResourceBlockSize <= 1 &&
1014 strVirtualQuantityUnits.compare(RTCString("count"), RTCString::CaseInsensitive) == 0
1015 )
1016 {
1017 throw OVFLogicError(N_("Element VirtualQuantityUnits is set to \"count\" "
1018 "while VirtualResourceBlockSize is set to 1. "
1019 "under %s element, line %d. "
1020 "It's needed to change on \"byte\". "
1021 "see DMTF Schema Documentation %s"),
1022 name.c_str(), ulLineNumber, DTMF_SPECS_URI);
1023 }
1024}
1025
1026void EthernetPortItem::fillItem(const xml::ElementNode *item)
1027{
1028 VirtualHardwareItem::fillItem(item);
1029
1030 xml::NodesLoop loopItemChildren(*item);// all child elements
1031 const xml::ElementNode *pelmItemChild;
1032 while ((pelmItemChild = loopItemChildren.forAllNodes()))
1033 {
1034 }
1035}
1036
1037void EthernetPortItem::_checkConsistencyAndCompliance() RT_THROW(OVFLogicError)
1038{
1039 VirtualHardwareItem::_checkConsistencyAndCompliance();
1040}
1041
1042////////////////////////////////////////////////////////////////////////////////
1043//
1044// Errors
1045//
1046////////////////////////////////////////////////////////////////////////////////
1047
1048OVFLogicError::OVFLogicError(const char *aFormat, ...)
1049{
1050 char *pszNewMsg;
1051 va_list args;
1052 va_start(args, aFormat);
1053 RTStrAPrintfV(&pszNewMsg, aFormat, args);
1054 setWhat(pszNewMsg);
1055 RTStrFree(pszNewMsg);
1056 va_end(args);
1057}
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