VirtualBox

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

Last change on this file since 48935 was 48009, checked in by vboxsync, 11 years ago

1024 * 1024 => _1M

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