VirtualBox

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

Last change on this file since 51498 was 50196, checked in by vboxsync, 11 years ago

Main: s/Vbox/VBox/g s/VB/VBox/g

  • 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 50196 2014-01-23 18:15:23Z vboxsync $ */
2/** @file
3 * OVF reader declarations.
4 *
5 * Depends only on IPRT, including the RTCString and IPRT XML classes.
6 */
7
8/*
9 * Copyright (C) 2008-2012 Oracle Corporation
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 */
19
20#include "ovfreader.h"
21
22using namespace std;
23using namespace ovf;
24
25////////////////////////////////////////////////////////////////////////////////
26//
27// OVF reader implementation
28//
29////////////////////////////////////////////////////////////////////////////////
30
31/**
32 * Constructor. This parses the given XML file out of the memory. Throws lots of exceptions
33 * on XML or OVF invalidity.
34 * @param pvBuf the memory buffer to parse
35 * @param cbSize the size of the memory buffer
36 * @param path path to a filename for error messages.
37 */
38OVFReader::OVFReader(const void *pvBuf, size_t cbSize, const RTCString &path)
39 : m_strPath(path)
40{
41 xml::XmlMemParser parser;
42 parser.read(pvBuf, cbSize,
43 m_strPath,
44 m_doc);
45 /* Start the parsing */
46 parse();
47}
48
49/**
50 * Constructor. This opens the given XML file and parses it. Throws lots of exceptions
51 * on XML or OVF invalidity.
52 * @param path
53 */
54OVFReader::OVFReader(const RTCString &path)
55 : m_strPath(path)
56{
57 xml::XmlFileParser parser;
58 parser.read(m_strPath,
59 m_doc);
60 /* Start the parsing */
61 parse();
62}
63
64void OVFReader::parse()
65{
66 const xml::ElementNode *pRootElem = m_doc.getRootElement();
67 const xml::AttributeNode *pTypeAttr;
68 const char *pcszTypeAttr = "";
69 RTCString pcszNamespaceURI;
70
71 if (!pRootElem || strcmp(pRootElem->getName(), "Envelope"))
72 throw OVFLogicError(N_("Root element in OVF file must be \"Envelope\"."));
73
74 pcszNamespaceURI = pRootElem->getNamespaceURI();
75 if(pcszNamespaceURI.isEmpty())
76 {
77 throw OVFLogicError(N_("Error reading namespace URI in 'Envelope' element, line %d"), pRootElem->getLineNumber());
78 }
79
80 if (strncmp(ovf::OVF20_URI_string, pcszNamespaceURI.c_str(), pcszNamespaceURI.length()) == 0)
81 {
82 m_envelopeData.setOVFVersion(ovf::OVFVersion_2_0);
83 }
84 else if (strncmp(OVF10_URI_string, pcszNamespaceURI.c_str(), pcszNamespaceURI.length()) == 0)
85 {
86 m_envelopeData.setOVFVersion(ovf::OVFVersion_1_0);
87 }
88 else
89 {
90 m_envelopeData.setOVFVersion(ovf::OVFVersion_0_9);
91 }
92
93 if ((pTypeAttr = pRootElem->findAttribute("lang", "xml")))
94 {
95 pcszTypeAttr = pTypeAttr->getValue();
96 m_envelopeData.lang = pcszTypeAttr;
97 }
98
99 // OVF has the following rough layout:
100 /*
101 -- <References> .... files referenced from other parts of the file, such as VMDK images
102 -- Metadata, comprised of several section commands
103 -- virtual machines, either a single <VirtualSystem>, or a <VirtualSystemCollection>
104 -- optionally <Strings> for localization
105 */
106
107 // get all "File" child elements of "References" section so we can look up files easily;
108 // first find the "References" sections so we can look up files
109 xml::ElementNodesList listFileElements; // receives all /Envelope/References/File nodes
110 const xml::ElementNode *pReferencesElem;
111 if ((pReferencesElem = pRootElem->findChildElement("References")))
112 pReferencesElem->getChildElements(listFileElements, "File");
113
114 // now go though the sections
115 LoopThruSections(pReferencesElem, pRootElem);
116}
117
118/**
119 * Private helper method that goes thru the elements of the given "current" element in the OVF XML
120 * and handles the contained child elements (which can be "Section" or "Content" elements).
121 *
122 * @param pcszPath Path spec of the XML file, for error messages.
123 * @param pReferencesElement "References" element from OVF, for looking up file specifications; can be NULL if no such element is present.
124 * @param pCurElem Element whose children are to be analyzed here.
125 * @return
126 */
127void OVFReader::LoopThruSections(const xml::ElementNode *pReferencesElem,
128 const xml::ElementNode *pCurElem)
129{
130 xml::NodesLoop loopChildren(*pCurElem);
131 const xml::ElementNode *pElem;
132 while ((pElem = loopChildren.forAllNodes()))
133 {
134 const char *pcszElemName = pElem->getName();
135 const xml::AttributeNode *pTypeAttr = pElem->findAttribute("type");
136 const char *pcszTypeAttr = pTypeAttr ? pTypeAttr->getValue() : "";
137
138 if ( !strcmp(pcszElemName, "DiskSection")
139 || ( !strcmp(pcszElemName, "Section")
140 && !strcmp(pcszTypeAttr, "ovf:DiskSection_Type")
141 )
142 )
143 {
144 HandleDiskSection(pReferencesElem, pElem);
145 }
146 else if ( !strcmp(pcszElemName, "NetworkSection")
147 || ( !strcmp(pcszElemName, "Section")
148 && !strcmp(pcszTypeAttr, "ovf:NetworkSection_Type")
149 )
150 )
151 {
152 HandleNetworkSection(pElem);
153 }
154 else if ( !strcmp(pcszElemName, "DeploymentOptionSection"))
155 {
156 // TODO
157 }
158 else if ( !strcmp(pcszElemName, "Info"))
159 {
160 // child of VirtualSystemCollection -- TODO
161 }
162 else if ( !strcmp(pcszElemName, "ResourceAllocationSection"))
163 {
164 // child of VirtualSystemCollection -- TODO
165 }
166 else if ( !strcmp(pcszElemName, "StartupSection"))
167 {
168 // child of VirtualSystemCollection -- TODO
169 }
170 else if ( !strcmp(pcszElemName, "VirtualSystem")
171 || ( !strcmp(pcszElemName, "Content")
172 && !strcmp(pcszTypeAttr, "ovf:VirtualSystem_Type")
173 )
174 )
175 {
176 HandleVirtualSystemContent(pElem);
177 }
178 else if ( !strcmp(pcszElemName, "VirtualSystemCollection")
179 || ( !strcmp(pcszElemName, "Content")
180 && !strcmp(pcszTypeAttr, "ovf:VirtualSystemCollection_Type")
181 )
182 )
183 {
184 // TODO ResourceAllocationSection
185
186 // recurse for this, since it has VirtualSystem elements as children
187 LoopThruSections(pReferencesElem, pElem);
188 }
189 }
190}
191
192/**
193 * Private helper method that handles disk sections in the OVF XML.
194 * Gets called indirectly from IAppliance::read().
195 *
196 * @param pcszPath Path spec of the XML file, for error messages.
197 * @param pReferencesElement "References" element from OVF, for looking up file specifications; can be NULL if no such element is present.
198 * @param pSectionElem Section element for which this helper is getting called.
199 * @return
200 */
201void OVFReader::HandleDiskSection(const xml::ElementNode *pReferencesElem,
202 const xml::ElementNode *pSectionElem)
203{
204 // contains "Disk" child elements
205 xml::NodesLoop loopDisks(*pSectionElem, "Disk");
206 const xml::ElementNode *pelmDisk;
207 while ((pelmDisk = loopDisks.forAllNodes()))
208 {
209 DiskImage d;
210 const char *pcszBad = NULL;
211 const char *pcszDiskId;
212 const char *pcszFormat;
213 if (!pelmDisk->getAttributeValue("diskId", pcszDiskId))
214 pcszBad = "diskId";
215 else if (!pelmDisk->getAttributeValue("format", pcszFormat))
216 pcszBad = "format";
217 else if (!pelmDisk->getAttributeValue("capacity", d.iCapacity))
218 pcszBad = "capacity";
219 else
220 {
221 d.strDiskId = pcszDiskId;
222 d.strFormat = pcszFormat;
223
224 if (!pelmDisk->getAttributeValue("populatedSize", d.iPopulatedSize))
225 // optional
226 d.iPopulatedSize = -1;
227
228 // optional vbox:uuid attribute (if OVF was exported by VirtualBox != 3.2)
229 pelmDisk->getAttributeValue("uuid", d.uuidVBox, "vbox");
230
231 const char *pcszFileRef;
232 if (pelmDisk->getAttributeValue("fileRef", pcszFileRef)) // optional
233 {
234 // look up corresponding /References/File nodes (list built above)
235 const xml::ElementNode *pFileElem;
236 if ( pReferencesElem
237 && (pFileElem = pReferencesElem->findChildElementFromId(pcszFileRef)) != NULL
238 )
239 {
240
241 // copy remaining values from file node then
242 const char *pcszBadInFile = NULL;
243 const char *pcszHref;
244 if (!pFileElem->getAttributeValue("href", pcszHref))
245 pcszBadInFile = "href";
246 else if (!pFileElem->getAttributeValue("size", d.iSize))
247 d.iSize = -1; // optional
248
249 d.strHref = pcszHref;
250
251 // if (!(pFileElem->getAttributeValue("size", d.iChunkSize))) TODO
252 d.iChunkSize = -1; // optional
253 const char *pcszCompression;
254 if (pFileElem->getAttributeValue("compression", pcszCompression))
255 d.strCompression = pcszCompression;
256
257 if (pcszBadInFile)
258 throw OVFLogicError(N_("Error reading \"%s\": missing or invalid attribute '%s' in 'File' element, line %d"),
259 m_strPath.c_str(),
260 pcszBadInFile,
261 pFileElem->getLineNumber());
262 }
263 else
264 throw OVFLogicError(N_("Error reading \"%s\": cannot find References/File element for ID '%s' referenced by 'Disk' element, line %d"),
265 m_strPath.c_str(),
266 pcszFileRef,
267 pelmDisk->getLineNumber());
268 }
269 }
270
271 if (pcszBad)
272 throw OVFLogicError(N_("Error reading \"%s\": missing or invalid attribute '%s' in 'DiskSection' element, line %d"),
273 m_strPath.c_str(),
274 pcszBad,
275 pelmDisk->getLineNumber());
276
277 // suggest a size in megabytes to help callers with progress reports
278 d.ulSuggestedSizeMB = 0;
279 if (d.iCapacity != -1)
280 d.ulSuggestedSizeMB = (uint32_t)(d.iCapacity / _1M);
281 else if (d.iPopulatedSize != -1)
282 d.ulSuggestedSizeMB = (uint32_t)(d.iPopulatedSize / _1M);
283 else if (d.iSize != -1)
284 d.ulSuggestedSizeMB = (uint32_t)(d.iSize / _1M);
285 if (d.ulSuggestedSizeMB == 0)
286 d.ulSuggestedSizeMB = 10000; // assume 10 GB, this is for the progress bar only anyway
287
288 m_mapDisks[d.strDiskId] = d;
289 }
290}
291
292/**
293 * Private helper method that handles network sections in the OVF XML.
294 * Gets called indirectly from IAppliance::read().
295 *
296 * @param pcszPath Path spec of the XML file, for error messages.
297 * @param pSectionElem Section element for which this helper is getting called.
298 * @return
299 */
300void OVFReader::HandleNetworkSection(const xml::ElementNode * /* pSectionElem */)
301{
302 // we ignore network sections for now
303
304// xml::NodesLoop loopNetworks(*pSectionElem, "Network");
305// const xml::Node *pelmNetwork;
306// while ((pelmNetwork = loopNetworks.forAllNodes()))
307// {
308// Network n;
309// if (!(pelmNetwork->getAttributeValue("name", n.strNetworkName)))
310// return setError(VBOX_E_FILE_ERROR,
311// tr("Error reading \"%s\": missing 'name' attribute in 'Network', line %d"),
312// pcszPath,
313// pelmNetwork->getLineNumber());
314//
315// m->mapNetworks[n.strNetworkName] = n;
316// }
317}
318
319/**
320 * Private helper method that handles a "VirtualSystem" element in the OVF XML.
321 * Gets called indirectly from IAppliance::read().
322 *
323 * @param pcszPath
324 * @param pContentElem
325 * @return
326 */
327void OVFReader::HandleVirtualSystemContent(const xml::ElementNode *pelmVirtualSystem)
328{
329 VirtualSystem vsys;
330
331 // peek under the <VirtualSystem> node whether we have a <vbox:Machine> node;
332 // that case case, the caller can completely ignore the OVF but only load the VBox machine XML
333 vsys.pelmVBoxMachine = pelmVirtualSystem->findChildElementNS("vbox", "Machine");
334
335 // now look for real OVF
336 const xml::AttributeNode *pIdAttr = pelmVirtualSystem->findAttribute("id");
337 if (pIdAttr)
338 vsys.strName = pIdAttr->getValue();
339
340 xml::NodesLoop loop(*pelmVirtualSystem); // all child elements
341 const xml::ElementNode *pelmThis;
342 while ((pelmThis = loop.forAllNodes()))
343 {
344 const char *pcszElemName = pelmThis->getName();
345 const char *pcszTypeAttr = "";
346 if (!strcmp(pcszElemName, "Section")) // OVF 0.9 used "Section" element always with a varying "type" attribute
347 {
348 const xml::AttributeNode *pTypeAttr = pelmThis->findAttribute("type");
349 if (pTypeAttr)
350 pcszTypeAttr = pTypeAttr->getValue();
351 else
352 throw OVFLogicError(N_("Error reading \"%s\": element \"Section\" has no \"type\" attribute, line %d"),
353 m_strPath.c_str(),
354 pelmThis->getLineNumber());
355 }
356
357 if ( !strcmp(pcszElemName, "EulaSection")
358 || !strcmp(pcszTypeAttr, "ovf:EulaSection_Type")
359 )
360 {
361 /* <EulaSection>
362 <Info ovf:msgid="6">License agreement for the Virtual System.</Info>
363 <License ovf:msgid="1">License terms can go in here.</License>
364 </EulaSection> */
365
366 const xml::ElementNode *pelmLicense;
367 if ((pelmLicense = pelmThis->findChildElement("License")))
368 vsys.strLicenseText = pelmLicense->getValue();
369 }
370 if ( !strcmp(pcszElemName, "ProductSection")
371 || !strcmp(pcszTypeAttr, "ovf:ProductSection_Type")
372 )
373 {
374 /* <Section ovf:required="false" xsi:type="ovf:ProductSection_Type">
375 <Info>Meta-information about the installed software</Info>
376 <Product>VAtest</Product>
377 <Vendor>SUN Microsystems</Vendor>
378 <Version>10.0</Version>
379 <ProductUrl>http://blogs.sun.com/VirtualGuru</ProductUrl>
380 <VendorUrl>http://www.sun.com</VendorUrl>
381 </Section> */
382 const xml::ElementNode *pelmProduct;
383 if ((pelmProduct = pelmThis->findChildElement("Product")))
384 vsys.strProduct = pelmProduct->getValue();
385 const xml::ElementNode *pelmVendor;
386 if ((pelmVendor = pelmThis->findChildElement("Vendor")))
387 vsys.strVendor = pelmVendor->getValue();
388 const xml::ElementNode *pelmVersion;
389 if ((pelmVersion = pelmThis->findChildElement("Version")))
390 vsys.strVersion = pelmVersion->getValue();
391 const xml::ElementNode *pelmProductUrl;
392 if ((pelmProductUrl = pelmThis->findChildElement("ProductUrl")))
393 vsys.strProductUrl = pelmProductUrl->getValue();
394 const xml::ElementNode *pelmVendorUrl;
395 if ((pelmVendorUrl = pelmThis->findChildElement("VendorUrl")))
396 vsys.strVendorUrl = pelmVendorUrl->getValue();
397 }
398 else if ( !strcmp(pcszElemName, "VirtualHardwareSection")
399 || !strcmp(pcszTypeAttr, "ovf:VirtualHardwareSection_Type")
400 )
401 {
402 const xml::ElementNode *pelmSystem, *pelmVirtualSystemType;
403 if ((pelmSystem = pelmThis->findChildElement("System")))
404 {
405 /* <System>
406 <vssd:Description>Description of the virtual hardware section.</vssd:Description>
407 <vssd:ElementName>vmware</vssd:ElementName>
408 <vssd:InstanceID>1</vssd:InstanceID>
409 <vssd:VirtualSystemIdentifier>MyLampService</vssd:VirtualSystemIdentifier>
410 <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType>
411 </System>*/
412 if ((pelmVirtualSystemType = pelmSystem->findChildElement("VirtualSystemType")))
413 vsys.strVirtualSystemType = pelmVirtualSystemType->getValue();
414 }
415
416 {
417 xml::NodesLoop loopVirtualHardwareItems(*pelmThis, "Item"); // all "Item" child elements
418 const xml::ElementNode *pelmItem;
419 while ((pelmItem = loopVirtualHardwareItems.forAllNodes()))
420 {
421 VirtualHardwareItem i;
422
423 i.ulLineNumber = pelmItem->getLineNumber();
424 i.fillItem(pelmItem);
425 try{
426 i.checkConsistencyAndCompliance();
427 }
428 catch (OVFLogicError &e)
429 {
430 throw OVFLogicError(N_("Error reading \"%s\": \"%s\""),
431 m_strPath.c_str(),
432 e.what());
433 }
434
435 // store!
436 vsys.mapHardwareItems[i.ulInstanceID] = i;
437 }
438 }
439
440 {
441 xml::NodesLoop loopVirtualHardwareItems(*pelmThis, "StorageItem");// all "StorageItem" child elements
442 const xml::ElementNode *pelmItem;
443 while ((pelmItem = loopVirtualHardwareItems.forAllNodes()))
444 {
445 StorageItem i;
446
447 i.ulLineNumber = pelmItem->getLineNumber();
448 i.fillItem(pelmItem);
449
450 try
451 {
452 i.checkConsistencyAndCompliance();
453 }
454 catch (OVFLogicError &e)
455 {
456 throw OVFLogicError(N_("Error reading \"%s\": \"%s\""),
457 m_strPath.c_str(),
458 e.what());
459 }
460
461 vsys.mapHardwareItems[i.ulInstanceID] = i;
462 }
463 }
464
465 {
466 xml::NodesLoop loopVirtualHardwareItems(*pelmThis, "EthernetPortItem");// all "EthernetPortItem" child elements
467 const xml::ElementNode *pelmItem;
468 while ((pelmItem = loopVirtualHardwareItems.forAllNodes()))
469 {
470 EthernetPortItem i;
471
472 i.ulLineNumber = pelmItem->getLineNumber();
473 i.fillItem(pelmItem);
474
475 try{
476 i.checkConsistencyAndCompliance();
477 }
478 catch (OVFLogicError &e)
479 {
480 throw OVFLogicError(N_("Error reading \"%s\": \"%s\""),
481 m_strPath.c_str(),
482 e.what());
483 }
484
485 vsys.mapHardwareItems[i.ulInstanceID] = i;
486 }
487 }
488
489 HardDiskController *pPrimaryIDEController = NULL;// will be set once found
490
491 // now go thru all hardware items and handle them according to their type;
492 // in this first loop we handle all items _except_ hard disk images,
493 // which we'll handle in a second loop below
494 HardwareItemsMap::const_iterator itH;
495 for (itH = vsys.mapHardwareItems.begin();
496 itH != vsys.mapHardwareItems.end();
497 ++itH)
498 {
499 const VirtualHardwareItem &i = itH->second;
500
501 // do some analysis
502 switch (i.resourceType)
503 {
504 case ResourceType_Processor: // 3
505 /* <rasd:Caption>1 virtual CPU</rasd:Caption>
506 <rasd:Description>Number of virtual CPUs</rasd:Description>
507 <rasd:ElementName>virtual CPU</rasd:ElementName>
508 <rasd:InstanceID>1</rasd:InstanceID>
509 <rasd:ResourceType>3</rasd:ResourceType>
510 <rasd:VirtualQuantity>1</rasd:VirtualQuantity>*/
511 if (i.ullVirtualQuantity < UINT16_MAX)
512 vsys.cCPUs = (uint16_t)i.ullVirtualQuantity;
513 else
514 throw OVFLogicError(N_("Error reading \"%s\": CPU count %RI64 is larger than %d, line %d"),
515 m_strPath.c_str(),
516 i.ullVirtualQuantity,
517 UINT16_MAX,
518 i.ulLineNumber);
519 break;
520
521 case ResourceType_Memory: // 4
522 if ( i.strAllocationUnits == "MegaBytes" // found in OVF created by OVF toolkit
523 || i.strAllocationUnits == "MB" // found in MS docs
524 || i.strAllocationUnits == "byte * 2^20" // suggested by OVF spec DSP0243 page 21
525 )
526 vsys.ullMemorySize = i.ullVirtualQuantity * _1M;
527 else
528 throw OVFLogicError(N_("Error reading \"%s\": Invalid allocation unit \"%s\" specified with memory size item, line %d"),
529 m_strPath.c_str(),
530 i.strAllocationUnits.c_str(),
531 i.ulLineNumber);
532 break;
533
534 case ResourceType_IDEController: // 5
535 {
536 /* <Item>
537 <rasd:Caption>ideController0</rasd:Caption>
538 <rasd:Description>IDE Controller</rasd:Description>
539 <rasd:InstanceId>5</rasd:InstanceId>
540 <rasd:ResourceType>5</rasd:ResourceType>
541 <rasd:Address>0</rasd:Address>
542 <rasd:BusNumber>0</rasd:BusNumber>
543 </Item> */
544 HardDiskController hdc;
545 hdc.system = HardDiskController::IDE;
546 hdc.idController = i.ulInstanceID;
547 hdc.strControllerType = i.strResourceSubType;
548
549 hdc.lAddress = i.lAddress;
550
551 if (!pPrimaryIDEController)
552 // this is the first IDE controller found: then mark it as "primary"
553 hdc.fPrimary = true;
554 else
555 {
556 // this is the second IDE controller found: If VMware exports two
557 // IDE controllers, it seems that they are given an "Address" of 0
558 // an 1, respectively, so assume address=0 means primary controller
559 if ( pPrimaryIDEController->lAddress == 0
560 && hdc.lAddress == 1
561 )
562 {
563 pPrimaryIDEController->fPrimary = true;
564 hdc.fPrimary = false;
565 }
566 else if ( pPrimaryIDEController->lAddress == 1
567 && hdc.lAddress == 0
568 )
569 {
570 pPrimaryIDEController->fPrimary = false;
571 hdc.fPrimary = false;
572 }
573 else
574 // then we really can't tell, just hope for the best
575 hdc.fPrimary = false;
576 }
577
578 vsys.mapControllers[i.ulInstanceID] = hdc;
579 if (!pPrimaryIDEController)
580 pPrimaryIDEController = &vsys.mapControllers[i.ulInstanceID];
581 break;
582 }
583
584 case ResourceType_ParallelSCSIHBA: // 6 SCSI controller
585 {
586 /* <Item>
587 <rasd:Caption>SCSI Controller 0 - LSI Logic</rasd:Caption>
588 <rasd:Description>SCI Controller</rasd:Description>
589 <rasd:ElementName>SCSI controller</rasd:ElementName>
590 <rasd:InstanceID>4</rasd:InstanceID>
591 <rasd:ResourceSubType>LsiLogic</rasd:ResourceSubType>
592 <rasd:ResourceType>6</rasd:ResourceType>
593 </Item> */
594 HardDiskController hdc;
595 hdc.system = HardDiskController::SCSI;
596 hdc.idController = i.ulInstanceID;
597 hdc.strControllerType = i.strResourceSubType;
598
599 vsys.mapControllers[i.ulInstanceID] = hdc;
600 break;
601 }
602
603 case ResourceType_EthernetAdapter: // 10
604 {
605 /* <Item>
606 <rasd:Caption>Ethernet adapter on 'Bridged'</rasd:Caption>
607 <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
608 <rasd:Connection>Bridged</rasd:Connection>
609 <rasd:InstanceID>6</rasd:InstanceID>
610 <rasd:ResourceType>10</rasd:ResourceType>
611 <rasd:ResourceSubType>E1000</rasd:ResourceSubType>
612 </Item>
613
614 OVF spec DSP 0243 page 21:
615 "For an Ethernet adapter, this specifies the abstract network connection name
616 for the virtual machine. All Ethernet adapters that specify the same abstract
617 network connection name within an OVF package shall be deployed on the same
618 network. The abstract network connection name shall be listed in the NetworkSection
619 at the outermost envelope level." */
620
621 // only store the name
622 EthernetAdapter ea;
623 ea.strAdapterType = i.strResourceSubType;
624 ea.strNetworkName = i.strConnection;
625 vsys.llEthernetAdapters.push_back(ea);
626 break;
627 }
628
629 case ResourceType_FloppyDrive: // 14
630 vsys.fHasFloppyDrive = true; // we have no additional information
631 break;
632
633 case ResourceType_CDDrive: // 15
634 /* <Item ovf:required="false">
635 <rasd:Caption>cdrom1</rasd:Caption>
636 <rasd:InstanceId>7</rasd:InstanceId>
637 <rasd:ResourceType>15</rasd:ResourceType>
638 <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
639 <rasd:Parent>5</rasd:Parent>
640 <rasd:AddressOnParent>0</rasd:AddressOnParent>
641 </Item> */
642 // I tried to see what happens if I set an ISO for the CD-ROM in VMware Workstation,
643 // but then the ovftool dies with "Device backing not supported". So I guess if
644 // VMware can't export ISOs, then we don't need to be able to import them right now.
645 vsys.fHasCdromDrive = true; // we have no additional information
646 break;
647
648 case ResourceType_HardDisk: // 17
649 // handled separately in second loop below
650 break;
651
652 case ResourceType_OtherStorageDevice: // 20 SATA controller
653 {
654 /* <Item>
655 <rasd:Description>SATA Controller</rasd:Description>
656 <rasd:Caption>sataController0</rasd:Caption>
657 <rasd:InstanceID>4</rasd:InstanceID>
658 <rasd:ResourceType>20</rasd:ResourceType>
659 <rasd:ResourceSubType>AHCI</rasd:ResourceSubType>
660 <rasd:Address>0</rasd:Address>
661 <rasd:BusNumber>0</rasd:BusNumber>
662 </Item> */
663 if ( i.strCaption.startsWith("sataController", RTCString::CaseInsensitive)
664 && !i.strResourceSubType.compare("AHCI", RTCString::CaseInsensitive)
665 )
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"),
676 m_strPath.c_str(),
677 ResourceType_OtherStorageDevice,
678 i.ulLineNumber);
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