VirtualBox

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

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

Main/xml/ovfreader.cpp: Fix OVF parsing of AHCI controller definition. Must not look at <caption>, because that tag is user defined content.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 46.8 KB
Line 
1/* $Id: ovfreader.cpp 60998 2016-05-17 11:36:44Z 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 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.strResourceSubType.compare("AHCI", RTCString::CaseInsensitive) == 0
665 || i.strResourceSubType.compare("vmware.sata.ahci", RTCString::CaseInsensitive) == 0)
666 {
667 HardDiskController hdc;
668 hdc.system = HardDiskController::SATA;
669 hdc.idController = i.ulInstanceID;
670 hdc.strControllerType = i.strResourceSubType;
671
672 vsys.mapControllers[i.ulInstanceID] = hdc;
673 }
674 else
675 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)"),
676 m_strPath.c_str(),
677 ResourceType_OtherStorageDevice,
678 i.ulLineNumber, i.strResourceSubType.c_str() );
679 break;
680 }
681
682 case ResourceType_USBController: // 23
683 /* <Item ovf:required="false">
684 <rasd:Caption>usb</rasd:Caption>
685 <rasd:Description>USB Controller</rasd:Description>
686 <rasd:InstanceId>3</rasd:InstanceId>
687 <rasd:ResourceType>23</rasd:ResourceType>
688 <rasd:Address>0</rasd:Address>
689 <rasd:BusNumber>0</rasd:BusNumber>
690 </Item> */
691 vsys.fHasUsbController = true; // we have no additional information
692 break;
693
694 case ResourceType_SoundCard: // 35
695 /* <Item ovf:required="false">
696 <rasd:Caption>sound</rasd:Caption>
697 <rasd:Description>Sound Card</rasd:Description>
698 <rasd:InstanceId>10</rasd:InstanceId>
699 <rasd:ResourceType>35</rasd:ResourceType>
700 <rasd:ResourceSubType>ensoniq1371</rasd:ResourceSubType>
701 <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
702 <rasd:AddressOnParent>3</rasd:AddressOnParent>
703 </Item> */
704 vsys.strSoundCardType = i.strResourceSubType;
705 break;
706
707 default:
708 {
709 /* If this unknown resource type isn't required, we simply skip it. */
710 if (i.fResourceRequired)
711 {
712 throw OVFLogicError(N_("Error reading \"%s\": Unknown resource type %d in hardware item, line %d"),
713 m_strPath.c_str(),
714 i.resourceType,
715 i.ulLineNumber);
716 }
717 }
718 } // end switch
719 }
720
721 // now run through the items for a second time, but handle only
722 // hard disk images; otherwise the code would fail if a hard
723 // disk image appears in the OVF before its hard disk controller
724 for (itH = vsys.mapHardwareItems.begin();
725 itH != vsys.mapHardwareItems.end();
726 ++itH)
727 {
728 const VirtualHardwareItem &i = itH->second;
729
730 // do some analysis
731 switch (i.resourceType)
732 {
733 case ResourceType_CDDrive: // 15
734 /* <Item ovf:required="false">
735 <rasd:Caption>cdrom1</rasd:Caption>
736 <rasd:InstanceId>7</rasd:InstanceId>
737 <rasd:ResourceType>15</rasd:ResourceType>
738 <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
739 <rasd:Parent>5</rasd:Parent>
740 <rasd:AddressOnParent>0</rasd:AddressOnParent>
741 </Item> */
742 case ResourceType_HardDisk: // 17
743 {
744 /* <Item>
745 <rasd:Caption>Harddisk 1</rasd:Caption>
746 <rasd:Description>HD</rasd:Description>
747 <rasd:ElementName>Hard Disk</rasd:ElementName>
748 <rasd:HostResource>ovf://disk/lamp</rasd:HostResource>
749 <rasd:InstanceID>5</rasd:InstanceID>
750 <rasd:Parent>4</rasd:Parent>
751 <rasd:ResourceType>17</rasd:ResourceType>
752 </Item> */
753
754 // look up the hard disk controller element whose InstanceID equals our Parent;
755 // this is how the connection is specified in OVF
756 ControllersMap::const_iterator it = vsys.mapControllers.find(i.ulParent);
757 if (it == vsys.mapControllers.end())
758 throw OVFLogicError(N_("Error reading \"%s\": Disk item with instance ID %d specifies invalid parent %d, line %d"),
759 m_strPath.c_str(),
760 i.ulInstanceID,
761 i.ulParent,
762 i.ulLineNumber);
763
764 VirtualDisk vd;
765 vd.idController = i.ulParent;
766 i.strAddressOnParent.toInt(vd.ulAddressOnParent);
767 // ovf://disk/lamp
768 // 123456789012345
769 if (i.strHostResource.startsWith("ovf://disk/"))
770 vd.strDiskId = i.strHostResource.substr(11);
771 else if (i.strHostResource.startsWith("ovf:/disk/"))
772 vd.strDiskId = i.strHostResource.substr(10);
773 else if (i.strHostResource.startsWith("/disk/"))
774 vd.strDiskId = i.strHostResource.substr(6);
775
776 //the error may be missed for CD, because CD can be empty
777 if ((vd.strDiskId.isEmpty() || (m_mapDisks.find(vd.strDiskId) == m_mapDisks.end()))
778 && i.resourceType == ResourceType_HardDisk)
779 throw OVFLogicError(N_("Error reading \"%s\": Disk item with instance ID %d specifies invalid host resource \"%s\", line %d"),
780 m_strPath.c_str(),
781 i.ulInstanceID,
782 i.strHostResource.c_str(),
783 i.ulLineNumber);
784
785 vsys.mapVirtualDisks[vd.strDiskId] = vd;
786 break;
787 }
788 default:
789 break;
790 }
791 }
792 }
793 else if ( !strcmp(pcszElemName, "OperatingSystemSection")
794 || !strcmp(pcszTypeAttr, "ovf:OperatingSystemSection_Type")
795 )
796 {
797 uint64_t cimos64;
798 if (!(pelmThis->getAttributeValue("id", cimos64)))
799 throw OVFLogicError(N_("Error reading \"%s\": missing or invalid 'ovf:id' attribute in operating system section element, line %d"),
800 m_strPath.c_str(),
801 pelmThis->getLineNumber());
802
803 vsys.cimos = (CIMOSType_T)cimos64;
804 const xml::ElementNode *pelmCIMOSDescription;
805 if ((pelmCIMOSDescription = pelmThis->findChildElement("Description")))
806 vsys.strCimosDesc = pelmCIMOSDescription->getValue();
807
808 const xml::ElementNode *pelmVBoxOSType;
809 if ((pelmVBoxOSType = pelmThis->findChildElementNS("vbox", // namespace
810 "OSType"))) // element name
811 vsys.strTypeVBox = pelmVBoxOSType->getValue();
812 }
813 else if ( (!strcmp(pcszElemName, "AnnotationSection"))
814 || (!strcmp(pcszTypeAttr, "ovf:AnnotationSection_Type"))
815 )
816 {
817 const xml::ElementNode *pelmAnnotation;
818 if ((pelmAnnotation = pelmThis->findChildElement("Annotation")))
819 vsys.strDescription = pelmAnnotation->getValue();
820 }
821 }
822
823 // now create the virtual system
824 m_llVirtualSystems.push_back(vsys);
825}
826
827void VirtualHardwareItem::fillItem(const xml::ElementNode *item)
828{
829 xml::NodesLoop loopItemChildren(*item);// all child elements
830 const xml::ElementNode *pelmItemChild;
831 while ((pelmItemChild = loopItemChildren.forAllNodes()))
832 {
833 const char *pcszItemChildName = pelmItemChild->getName();
834 if (!strcmp(pcszItemChildName, "Description"))
835 strDescription = pelmItemChild->getValue();
836 else if (!strcmp(pcszItemChildName, "Caption"))
837 strCaption = pelmItemChild->getValue();
838 else if (!strcmp(pcszItemChildName, "ElementName"))
839 strElementName = pelmItemChild->getValue();
840 else if ((!strcmp(pcszItemChildName, "InstanceID"))
841 ||(!strcmp(pcszItemChildName, "InstanceId"))
842 )
843 pelmItemChild->copyValue(ulInstanceID);
844 else if (!strcmp(pcszItemChildName, "HostResource"))
845 strHostResource = pelmItemChild->getValue();
846 else if (!strcmp(pcszItemChildName, "ResourceType"))
847 {
848 uint32_t ulType;
849 pelmItemChild->copyValue(ulType);
850 resourceType = (ResourceType_T)ulType;
851 fResourceRequired = true;
852 const char *pcszAttValue;
853 if (item->getAttributeValue("required", pcszAttValue))
854 {
855 if (!strcmp(pcszAttValue, "false"))
856 fResourceRequired = false;
857 }
858 }
859 else if (!strcmp(pcszItemChildName, "OtherResourceType"))
860 strOtherResourceType = pelmItemChild->getValue();
861 else if (!strcmp(pcszItemChildName, "ResourceSubType"))
862 strResourceSubType = pelmItemChild->getValue();
863 else if (!strcmp(pcszItemChildName, "AutomaticAllocation"))
864 fAutomaticAllocation = (!strcmp(pelmItemChild->getValue(), "true")) ? true : false;
865 else if (!strcmp(pcszItemChildName, "AutomaticDeallocation"))
866 fAutomaticDeallocation = (!strcmp(pelmItemChild->getValue(), "true")) ? true : false;
867 else if (!strcmp(pcszItemChildName, "Parent"))
868 pelmItemChild->copyValue(ulParent);
869 else if (!strcmp(pcszItemChildName, "Connection"))
870 strConnection = pelmItemChild->getValue();
871 else if (!strcmp(pcszItemChildName, "Address"))
872 {
873 strAddress = pelmItemChild->getValue();
874 pelmItemChild->copyValue(lAddress);
875 }
876 else if (!strcmp(pcszItemChildName, "AddressOnParent"))
877 strAddressOnParent = pelmItemChild->getValue();
878 else if (!strcmp(pcszItemChildName, "AllocationUnits"))
879 strAllocationUnits = pelmItemChild->getValue();
880 else if (!strcmp(pcszItemChildName, "VirtualQuantity"))
881 pelmItemChild->copyValue(ullVirtualQuantity);
882 else if (!strcmp(pcszItemChildName, "Reservation"))
883 pelmItemChild->copyValue(ullReservation);
884 else if (!strcmp(pcszItemChildName, "Limit"))
885 pelmItemChild->copyValue(ullLimit);
886 else if (!strcmp(pcszItemChildName, "Weight"))
887 pelmItemChild->copyValue(ullWeight);
888 else if (!strcmp(pcszItemChildName, "ConsumerVisibility"))
889 strConsumerVisibility = pelmItemChild->getValue();
890 else if (!strcmp(pcszItemChildName, "MappingBehavior"))
891 strMappingBehavior = pelmItemChild->getValue();
892 else if (!strcmp(pcszItemChildName, "PoolID"))
893 strPoolID = pelmItemChild->getValue();
894 else if (!strcmp(pcszItemChildName, "BusNumber"))
895 pelmItemChild->copyValue(ulBusNumber);
896// else if (pelmItemChild->getPrefix() == NULL
897// || strcmp(pelmItemChild->getPrefix(), "vmw"))
898// throw OVFLogicError(N_("Unknown element \"%s\" under Item element, line %d"),
899// pcszItemChildName,
900// ulLineNumber);
901 }
902}
903
904void VirtualHardwareItem::_checkConsistencyAndCompliance() RT_THROW(OVFLogicError)
905{
906 RTCString name = getItemName();
907 if (ulInstanceID == 0)
908 throw OVFLogicError(N_("Element InstanceID is absent under %s element, line %d. "
909 "see DMTF Schema Documentation %s"),
910 name.c_str(), ulLineNumber, DTMF_SPECS_URI);
911 if (resourceType == 0)
912 throw OVFLogicError(N_("Empty element ResourceType under %s element, line %d. "
913 "see DMTF Schema Documentation %s"),
914 name.c_str(), ulLineNumber, DTMF_SPECS_URI);
915}
916
917void StorageItem::fillItem(const xml::ElementNode *item)
918{
919 VirtualHardwareItem::fillItem(item);
920
921 xml::NodesLoop loopItemChildren(*item);// all child elements
922 const xml::ElementNode *pelmItemChild;
923 while ((pelmItemChild = loopItemChildren.forAllNodes()))
924 {
925 const char *pcszItemChildName = pelmItemChild->getName();
926 if (!strcmp(pcszItemChildName, "HostExtentName"))
927 strHostExtentName = pelmItemChild->getValue();
928 else if (!strcmp(pcszItemChildName, "OtherHostExtentNameFormat"))
929 strOtherHostExtentNameFormat = pelmItemChild->getValue();
930 else if (!strcmp(pcszItemChildName, "OtherHostExtentNameNamespace"))
931 strOtherHostExtentNameNamespace = pelmItemChild->getValue();
932 else if (!strcmp(pcszItemChildName, "VirtualQuantityUnits"))
933 strVirtualQuantityUnits = pelmItemChild->getValue();
934 else if (!strcmp(pcszItemChildName, "Access"))
935 {
936 uint32_t temp;
937 pelmItemChild->copyValue(temp);
938 accessType = (StorageAccessType_T)temp;
939 }
940 else if (!strcmp(pcszItemChildName, "HostExtentNameFormat"))
941 {
942 }
943 else if (!strcmp(pcszItemChildName, "HostExtentNameNamespace"))
944 {
945 }
946 else if (!strcmp(pcszItemChildName, "HostExtentStartingAddress"))
947 {
948 }
949 else if (!strcmp(pcszItemChildName, "HostResourceBlockSize"))
950 {
951 int64_t temp;
952 pelmItemChild->copyValue(temp);
953 hostResourceBlockSize = temp;
954 }
955 else if (!strcmp(pcszItemChildName, "Limit"))
956 {
957 int64_t temp;
958 pelmItemChild->copyValue(temp);
959 limit = temp;
960 }
961 else if (!strcmp(pcszItemChildName, "Reservation"))
962 {
963 int64_t temp;
964 pelmItemChild->copyValue(temp);
965 reservation = temp;
966 }
967 else if (!strcmp(pcszItemChildName, "VirtualQuantity"))
968 {
969 int64_t temp;
970 pelmItemChild->copyValue(temp);
971 virtualQuantity = temp;
972 }
973 else if (!strcmp(pcszItemChildName, "VirtualResourceBlockSize"))
974 {
975 int64_t temp;
976 pelmItemChild->copyValue(temp);
977 virtualResourceBlockSize = temp;
978 }
979 }
980}
981
982
983void StorageItem::_checkConsistencyAndCompliance() RT_THROW(OVFLogicError)
984{
985 VirtualHardwareItem::_checkConsistencyAndCompliance();
986
987 RTCString name = getItemName();
988
989 if (accessType == StorageAccessType_Unknown)
990 {
991 //throw OVFLogicError(N_("Access type is unknown under %s element, line %d"),
992 // name.c_str(), ulLineNumber);
993 }
994
995 if (hostResourceBlockSize <= 0 && reservation > 0)
996 {
997 throw OVFLogicError(N_("Element HostResourceBlockSize is absent under %s element, line %d. "
998 "see DMTF Schema Documentation %s"),
999 name.c_str(), ulLineNumber, DTMF_SPECS_URI);
1000 }
1001
1002 if (virtualResourceBlockSize <= 0 && virtualQuantity > 0)
1003 {
1004 throw OVFLogicError(N_("Element VirtualResourceBlockSize is absent under %s element, line %d. "
1005 "see DMTF Schema Documentation %s"),
1006 name.c_str(), ulLineNumber, DTMF_SPECS_URI);
1007 }
1008
1009 if (virtualQuantity > 0 && strVirtualQuantityUnits.isEmpty())
1010 {
1011 throw OVFLogicError(N_("Element VirtualQuantityUnits is absent under %s element, line %d. "
1012 "see DMTF Schema Documentation %s"),
1013 name.c_str(), ulLineNumber, DTMF_SPECS_URI);
1014 }
1015
1016 if (virtualResourceBlockSize <= 1 &&
1017 strVirtualQuantityUnits.compare(RTCString("count"), RTCString::CaseInsensitive) == 0
1018 )
1019 {
1020 throw OVFLogicError(N_("Element VirtualQuantityUnits is set to \"count\" "
1021 "while VirtualResourceBlockSize is set to 1. "
1022 "under %s element, line %d. "
1023 "It's needed to change on \"byte\". "
1024 "see DMTF Schema Documentation %s"),
1025 name.c_str(), ulLineNumber, DTMF_SPECS_URI);
1026 }
1027}
1028
1029void EthernetPortItem::fillItem(const xml::ElementNode *item)
1030{
1031 VirtualHardwareItem::fillItem(item);
1032
1033 xml::NodesLoop loopItemChildren(*item);// all child elements
1034 const xml::ElementNode *pelmItemChild;
1035 while ((pelmItemChild = loopItemChildren.forAllNodes()))
1036 {
1037 }
1038}
1039
1040void EthernetPortItem::_checkConsistencyAndCompliance() RT_THROW(OVFLogicError)
1041{
1042 VirtualHardwareItem::_checkConsistencyAndCompliance();
1043}
1044
1045////////////////////////////////////////////////////////////////////////////////
1046//
1047// Errors
1048//
1049////////////////////////////////////////////////////////////////////////////////
1050
1051OVFLogicError::OVFLogicError(const char *aFormat, ...)
1052{
1053 char *pszNewMsg;
1054 va_list args;
1055 va_start(args, aFormat);
1056 RTStrAPrintfV(&pszNewMsg, aFormat, args);
1057 setWhat(pszNewMsg);
1058 RTStrFree(pszNewMsg);
1059 va_end(args);
1060}
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