VirtualBox

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

Last change on this file since 95353 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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