VirtualBox

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

Last change on this file since 28669 was 28531, checked in by vboxsync, 15 years ago

Main/OVF: fix import of IDE controllers (export currently broken)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 36.2 KB
Line 
1/* $Id: ovfreader.cpp 28531 2010-04-20 16:22:41Z vboxsync $ */
2/** @file
3 *
4 * OVF reader declarations. Depends only on IPRT, including the iprt::MiniString
5 * and IPRT XML classes.
6 */
7
8/*
9 * Copyright (C) 2008-2009 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
20 * Clara, CA 95054 USA or visit http://www.sun.com if you need
21 * additional information or have any questions.
22 */
23
24#include "ovfreader.h"
25
26using namespace std;
27using namespace iprt;
28using namespace ovf;
29
30////////////////////////////////////////////////////////////////////////////////
31//
32// OVF reader implemenation
33//
34////////////////////////////////////////////////////////////////////////////////
35
36/**
37 * Constructor. This opens the given XML file and parses it. Throws lots of exceptions
38 * on XML or OVF invalidity.
39 * @param path
40 */
41OVFReader::OVFReader(const MiniString &path)
42 : m_strPath(path)
43{
44 xml::XmlFileParser parser;
45 parser.read(m_strPath,
46 m_doc);
47
48 const xml::ElementNode *pRootElem = m_doc.getRootElement();
49 if ( !pRootElem
50 || strcmp(pRootElem->getName(), "Envelope")
51 )
52 throw OVFLogicError(N_("Root element in OVF file must be \"Envelope\"."));
53
54 // OVF has the following rough layout:
55 /*
56 -- <References> .... files referenced from other parts of the file, such as VMDK images
57 -- Metadata, comprised of several section commands
58 -- virtual machines, either a single <VirtualSystem>, or a <VirtualSystemCollection>
59 -- optionally <Strings> for localization
60 */
61
62 // get all "File" child elements of "References" section so we can look up files easily;
63 // first find the "References" sections so we can look up files
64 xml::ElementNodesList listFileElements; // receives all /Envelope/References/File nodes
65 const xml::ElementNode *pReferencesElem;
66 if ((pReferencesElem = pRootElem->findChildElement("References")))
67 pReferencesElem->getChildElements(listFileElements, "File");
68
69 // now go though the sections
70 LoopThruSections(pReferencesElem, pRootElem);
71}
72
73/**
74 * Private helper method that goes thru the elements of the given "current" element in the OVF XML
75 * and handles the contained child elements (which can be "Section" or "Content" elements).
76 *
77 * @param pcszPath Path spec of the XML file, for error messages.
78 * @param pReferencesElement "References" element from OVF, for looking up file specifications; can be NULL if no such element is present.
79 * @param pCurElem Element whose children are to be analyzed here.
80 * @return
81 */
82void OVFReader::LoopThruSections(const xml::ElementNode *pReferencesElem,
83 const xml::ElementNode *pCurElem)
84{
85 xml::NodesLoop loopChildren(*pCurElem);
86 const xml::ElementNode *pElem;
87 while ((pElem = loopChildren.forAllNodes()))
88 {
89 const char *pcszElemName = pElem->getName();
90 const char *pcszTypeAttr = "";
91 const xml::AttributeNode *pTypeAttr;
92 if ((pTypeAttr = pElem->findAttribute("type")))
93 pcszTypeAttr = pTypeAttr->getValue();
94
95 if ( (!strcmp(pcszElemName, "DiskSection"))
96 || ( (!strcmp(pcszElemName, "Section"))
97 && (!strcmp(pcszTypeAttr, "ovf:DiskSection_Type"))
98 )
99 )
100 {
101 HandleDiskSection(pReferencesElem, pElem);
102 }
103 else if ( (!strcmp(pcszElemName, "NetworkSection"))
104 || ( (!strcmp(pcszElemName, "Section"))
105 && (!strcmp(pcszTypeAttr, "ovf:NetworkSection_Type"))
106 )
107 )
108 {
109 HandleNetworkSection(pElem);
110 }
111 else if ( (!strcmp(pcszElemName, "DeploymentOptionSection")))
112 {
113 // TODO
114 }
115 else if ( (!strcmp(pcszElemName, "Info")))
116 {
117 // child of VirtualSystemCollection -- TODO
118 }
119 else if ( (!strcmp(pcszElemName, "ResourceAllocationSection")))
120 {
121 // child of VirtualSystemCollection -- TODO
122 }
123 else if ( (!strcmp(pcszElemName, "StartupSection")))
124 {
125 // child of VirtualSystemCollection -- TODO
126 }
127 else if ( (!strcmp(pcszElemName, "VirtualSystem"))
128 || ( (!strcmp(pcszElemName, "Content"))
129 && (!strcmp(pcszTypeAttr, "ovf:VirtualSystem_Type"))
130 )
131 )
132 {
133 HandleVirtualSystemContent(pElem);
134 }
135 else if ( (!strcmp(pcszElemName, "VirtualSystemCollection"))
136 || ( (!strcmp(pcszElemName, "Content"))
137 && (!strcmp(pcszTypeAttr, "ovf:VirtualSystemCollection_Type"))
138 )
139 )
140 {
141 // TODO ResourceAllocationSection
142
143 // recurse for this, since it has VirtualSystem elements as children
144 LoopThruSections(pReferencesElem, pElem);
145 }
146 }
147}
148
149/**
150 * Private helper method that handles disk sections in the OVF XML.
151 * Gets called indirectly from IAppliance::read().
152 *
153 * @param pcszPath Path spec of the XML file, for error messages.
154 * @param pReferencesElement "References" element from OVF, for looking up file specifications; can be NULL if no such element is present.
155 * @param pSectionElem Section element for which this helper is getting called.
156 * @return
157 */
158void OVFReader::HandleDiskSection(const xml::ElementNode *pReferencesElem,
159 const xml::ElementNode *pSectionElem)
160{
161 // contains "Disk" child elements
162 xml::NodesLoop loopDisks(*pSectionElem, "Disk");
163 const xml::ElementNode *pelmDisk;
164 while ((pelmDisk = loopDisks.forAllNodes()))
165 {
166 DiskImage d;
167 const char *pcszBad = NULL;
168 const char *pcszDiskId;
169 const char *pcszFormat;
170 if (!(pelmDisk->getAttributeValue("diskId", pcszDiskId)))
171 pcszBad = "diskId";
172 else if (!(pelmDisk->getAttributeValue("format", pcszFormat)))
173 pcszBad = "format";
174 else if (!(pelmDisk->getAttributeValue("capacity", d.iCapacity)))
175 pcszBad = "capacity";
176 else
177 {
178 d.strDiskId = pcszDiskId;
179 d.strFormat = pcszFormat;
180
181 if (!(pelmDisk->getAttributeValue("populatedSize", d.iPopulatedSize)))
182 // optional
183 d.iPopulatedSize = -1;
184
185 // optional vbox:uuid attribute (if OVF was exported by VirtualBox != 3.2)
186 pelmDisk->getAttributeValue("vbox:uuid", d.uuidVbox);
187
188 const char *pcszFileRef;
189 if (pelmDisk->getAttributeValue("fileRef", pcszFileRef)) // optional
190 {
191 // look up corresponding /References/File nodes (list built above)
192 const xml::ElementNode *pFileElem;
193 if ( pReferencesElem
194 && ((pFileElem = pReferencesElem->findChildElementFromId(pcszFileRef)))
195 )
196 {
197 // copy remaining values from file node then
198 const char *pcszBadInFile = NULL;
199 const char *pcszHref;
200 if (!(pFileElem->getAttributeValue("href", pcszHref)))
201 pcszBadInFile = "href";
202 else if (!(pFileElem->getAttributeValue("size", d.iSize)))
203 d.iSize = -1; // optional
204
205 d.strHref = pcszHref;
206
207 // if (!(pFileElem->getAttributeValue("size", d.iChunkSize))) TODO
208 d.iChunkSize = -1; // optional
209 const char *pcszCompression;
210 if (pFileElem->getAttributeValue("compression", pcszCompression))
211 d.strCompression = pcszCompression;
212
213 if (pcszBadInFile)
214 throw OVFLogicError(N_("Error reading \"%s\": missing or invalid attribute '%s' in 'File' element, line %d"),
215 m_strPath.c_str(),
216 pcszBadInFile,
217 pFileElem->getLineNumber());
218 }
219 else
220 throw OVFLogicError(N_("Error reading \"%s\": cannot find References/File element for ID '%s' referenced by 'Disk' element, line %d"),
221 m_strPath.c_str(),
222 pcszFileRef,
223 pelmDisk->getLineNumber());
224 }
225 }
226
227 if (pcszBad)
228 throw OVFLogicError(N_("Error reading \"%s\": missing or invalid attribute '%s' in 'DiskSection' element, line %d"),
229 m_strPath.c_str(),
230 pcszBad,
231 pelmDisk->getLineNumber());
232
233 // suggest a size in megabytes to help callers with progress reports
234 d.ulSuggestedSizeMB = 0;
235 if (d.iCapacity != -1)
236 d.ulSuggestedSizeMB = d.iCapacity / _1M;
237 else if (d.iPopulatedSize != -1)
238 d.ulSuggestedSizeMB = d.iPopulatedSize / _1M;
239 else if (d.iSize != -1)
240 d.ulSuggestedSizeMB = d.iSize / _1M;
241 if (d.ulSuggestedSizeMB == 0)
242 d.ulSuggestedSizeMB = 10000; // assume 10 GB, this is for the progress bar only anyway
243
244 m_mapDisks[d.strDiskId] = d;
245 }
246}
247
248/**
249 * Private helper method that handles network sections in the OVF XML.
250 * Gets called indirectly from IAppliance::read().
251 *
252 * @param pcszPath Path spec of the XML file, for error messages.
253 * @param pSectionElem Section element for which this helper is getting called.
254 * @return
255 */
256void OVFReader::HandleNetworkSection(const xml::ElementNode * /* pSectionElem */)
257{
258 // we ignore network sections for now
259
260// xml::NodesLoop loopNetworks(*pSectionElem, "Network");
261// const xml::Node *pelmNetwork;
262// while ((pelmNetwork = loopNetworks.forAllNodes()))
263// {
264// Network n;
265// if (!(pelmNetwork->getAttributeValue("name", n.strNetworkName)))
266// return setError(VBOX_E_FILE_ERROR,
267// tr("Error reading \"%s\": missing 'name' attribute in 'Network', line %d"),
268// pcszPath,
269// pelmNetwork->getLineNumber());
270//
271// m->mapNetworks[n.strNetworkName] = n;
272// }
273}
274
275/**
276 * Private helper method that handles a "VirtualSystem" element in the OVF XML.
277 * Gets called indirectly from IAppliance::read().
278 *
279 * @param pcszPath
280 * @param pContentElem
281 * @return
282 */
283void OVFReader::HandleVirtualSystemContent(const xml::ElementNode *pelmVirtualSystem)
284{
285 VirtualSystem vsys;
286
287 // peek under the <VirtualSystem> node whether we have a <vbox:Machine> node;
288 // that case case, the caller can completely ignore the OVF but only load the VBox machine XML
289 vsys.pelmVboxMachine = pelmVirtualSystem->findChildElement("vbox", "Machine");
290
291 // now look for real OVF
292 const xml::AttributeNode *pIdAttr = pelmVirtualSystem->findAttribute("id");
293 if (pIdAttr)
294 vsys.strName = pIdAttr->getValue();
295
296 xml::NodesLoop loop(*pelmVirtualSystem); // all child elements
297 const xml::ElementNode *pelmThis;
298 while ((pelmThis = loop.forAllNodes()))
299 {
300 const char *pcszElemName = pelmThis->getName();
301 const xml::AttributeNode *pTypeAttr = pelmThis->findAttribute("type");
302 const char *pcszTypeAttr = (pTypeAttr) ? pTypeAttr->getValue() : "";
303
304 if ( (!strcmp(pcszElemName, "EulaSection"))
305 || (!strcmp(pcszTypeAttr, "ovf:EulaSection_Type"))
306 )
307 {
308 /* <EulaSection>
309 <Info ovf:msgid="6">License agreement for the Virtual System.</Info>
310 <License ovf:msgid="1">License terms can go in here.</License>
311 </EulaSection> */
312
313 const xml::ElementNode *pelmLicense;
314 if ((pelmLicense = pelmThis->findChildElement("License")))
315 vsys.strLicenseText = pelmLicense->getValue();
316 }
317 if ( (!strcmp(pcszElemName, "ProductSection"))
318 || (!strcmp(pcszTypeAttr, "ovf:ProductSection_Type"))
319 )
320 {
321 /* <Section ovf:required="false" xsi:type="ovf:ProductSection_Type">
322 <Info>Meta-information about the installed software</Info>
323 <Product>VAtest</Product>
324 <Vendor>SUN Microsystems</Vendor>
325 <Version>10.0</Version>
326 <ProductUrl>http://blogs.sun.com/VirtualGuru</ProductUrl>
327 <VendorUrl>http://www.sun.com</VendorUrl>
328 </Section> */
329 const xml::ElementNode *pelmProduct;
330 if ((pelmProduct = pelmThis->findChildElement("Product")))
331 vsys.strProduct = pelmProduct->getValue();
332 const xml::ElementNode *pelmVendor;
333 if ((pelmVendor = pelmThis->findChildElement("Vendor")))
334 vsys.strVendor = pelmVendor->getValue();
335 const xml::ElementNode *pelmVersion;
336 if ((pelmVersion = pelmThis->findChildElement("Version")))
337 vsys.strVersion = pelmVersion->getValue();
338 const xml::ElementNode *pelmProductUrl;
339 if ((pelmProductUrl = pelmThis->findChildElement("ProductUrl")))
340 vsys.strProductUrl = pelmProductUrl->getValue();
341 const xml::ElementNode *pelmVendorUrl;
342 if ((pelmVendorUrl = pelmThis->findChildElement("VendorUrl")))
343 vsys.strVendorUrl = pelmVendorUrl->getValue();
344 }
345 else if ( (!strcmp(pcszElemName, "VirtualHardwareSection"))
346 || (!strcmp(pcszTypeAttr, "ovf:VirtualHardwareSection_Type"))
347 )
348 {
349 const xml::ElementNode *pelmSystem, *pelmVirtualSystemType;
350 if ((pelmSystem = pelmThis->findChildElement("System")))
351 {
352 /* <System>
353 <vssd:Description>Description of the virtual hardware section.</vssd:Description>
354 <vssd:ElementName>vmware</vssd:ElementName>
355 <vssd:InstanceID>1</vssd:InstanceID>
356 <vssd:VirtualSystemIdentifier>MyLampService</vssd:VirtualSystemIdentifier>
357 <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType>
358 </System>*/
359 if ((pelmVirtualSystemType = pelmSystem->findChildElement("VirtualSystemType")))
360 vsys.strVirtualSystemType = pelmVirtualSystemType->getValue();
361 }
362
363 xml::NodesLoop loopVirtualHardwareItems(*pelmThis, "Item"); // all "Item" child elements
364 const xml::ElementNode *pelmItem;
365 while ((pelmItem = loopVirtualHardwareItems.forAllNodes()))
366 {
367 VirtualHardwareItem i;
368
369 i.ulLineNumber = pelmItem->getLineNumber();
370
371 xml::NodesLoop loopItemChildren(*pelmItem); // all child elements
372 const xml::ElementNode *pelmItemChild;
373 while ((pelmItemChild = loopItemChildren.forAllNodes()))
374 {
375 const char *pcszItemChildName = pelmItemChild->getName();
376 if (!strcmp(pcszItemChildName, "Description"))
377 i.strDescription = pelmItemChild->getValue();
378 else if (!strcmp(pcszItemChildName, "Caption"))
379 i.strCaption = pelmItemChild->getValue();
380 else if (!strcmp(pcszItemChildName, "ElementName"))
381 i.strElementName = pelmItemChild->getValue();
382 else if ( (!strcmp(pcszItemChildName, "InstanceID"))
383 || (!strcmp(pcszItemChildName, "InstanceId"))
384 )
385 pelmItemChild->copyValue(i.ulInstanceID);
386 else if (!strcmp(pcszItemChildName, "HostResource"))
387 i.strHostResource = pelmItemChild->getValue();
388 else if (!strcmp(pcszItemChildName, "ResourceType"))
389 {
390 uint32_t ulType;
391 pelmItemChild->copyValue(ulType);
392 i.resourceType = (ResourceType_T)ulType;
393 }
394 else if (!strcmp(pcszItemChildName, "OtherResourceType"))
395 i.strOtherResourceType = pelmItemChild->getValue();
396 else if (!strcmp(pcszItemChildName, "ResourceSubType"))
397 i.strResourceSubType = pelmItemChild->getValue();
398 else if (!strcmp(pcszItemChildName, "AutomaticAllocation"))
399 i.fAutomaticAllocation = (!strcmp(pelmItemChild->getValue(), "true")) ? true : false;
400 else if (!strcmp(pcszItemChildName, "AutomaticDeallocation"))
401 i.fAutomaticDeallocation = (!strcmp(pelmItemChild->getValue(), "true")) ? true : false;
402 else if (!strcmp(pcszItemChildName, "Parent"))
403 pelmItemChild->copyValue(i.ulParent);
404 else if (!strcmp(pcszItemChildName, "Connection"))
405 i.strConnection = pelmItemChild->getValue();
406 else if (!strcmp(pcszItemChildName, "Address"))
407 i.strAddress = pelmItemChild->getValue();
408 else if (!strcmp(pcszItemChildName, "AddressOnParent"))
409 i.strAddressOnParent = pelmItemChild->getValue();
410 else if (!strcmp(pcszItemChildName, "AllocationUnits"))
411 i.strAllocationUnits = pelmItemChild->getValue();
412 else if (!strcmp(pcszItemChildName, "VirtualQuantity"))
413 pelmItemChild->copyValue(i.ullVirtualQuantity);
414 else if (!strcmp(pcszItemChildName, "Reservation"))
415 pelmItemChild->copyValue(i.ullReservation);
416 else if (!strcmp(pcszItemChildName, "Limit"))
417 pelmItemChild->copyValue(i.ullLimit);
418 else if (!strcmp(pcszItemChildName, "Weight"))
419 pelmItemChild->copyValue(i.ullWeight);
420 else if (!strcmp(pcszItemChildName, "ConsumerVisibility"))
421 i.strConsumerVisibility = pelmItemChild->getValue();
422 else if (!strcmp(pcszItemChildName, "MappingBehavior"))
423 i.strMappingBehavior = pelmItemChild->getValue();
424 else if (!strcmp(pcszItemChildName, "PoolID"))
425 i.strPoolID = pelmItemChild->getValue();
426 else if (!strcmp(pcszItemChildName, "BusNumber")) // seen in some old OVF, but it's not listed in the OVF specs
427 pelmItemChild->copyValue(i.ulBusNumber);
428 else
429 throw OVFLogicError(N_("Error reading \"%s\": unknown element \"%s\" under Item element, line %d"),
430 m_strPath.c_str(),
431 pcszItemChildName,
432 i.ulLineNumber);
433 }
434
435 // store!
436 vsys.mapHardwareItems[i.ulInstanceID] = i;
437 }
438
439 // now go thru all hardware items and handle them according to their type;
440 // in this first loop we handle all items _except_ hard disk images,
441 // which we'll handle in a second loop below
442 HardwareItemsMap::const_iterator itH;
443 for (itH = vsys.mapHardwareItems.begin();
444 itH != vsys.mapHardwareItems.end();
445 ++itH)
446 {
447 const VirtualHardwareItem &i = itH->second;
448
449 // do some analysis
450 switch (i.resourceType)
451 {
452 case ResourceType_Processor: // 3
453 /* <rasd:Caption>1 virtual CPU</rasd:Caption>
454 <rasd:Description>Number of virtual CPUs</rasd:Description>
455 <rasd:ElementName>virtual CPU</rasd:ElementName>
456 <rasd:InstanceID>1</rasd:InstanceID>
457 <rasd:ResourceType>3</rasd:ResourceType>
458 <rasd:VirtualQuantity>1</rasd:VirtualQuantity>*/
459 if (i.ullVirtualQuantity < UINT16_MAX)
460 vsys.cCPUs = (uint16_t)i.ullVirtualQuantity;
461 else
462 throw OVFLogicError(N_("Error reading \"%s\": CPU count %RI64 is larger than %d, line %d"),
463 m_strPath.c_str(),
464 i.ullVirtualQuantity,
465 UINT16_MAX,
466 i.ulLineNumber);
467 break;
468
469 case ResourceType_Memory: // 4
470 if ( (i.strAllocationUnits == "MegaBytes") // found in OVF created by OVF toolkit
471 || (i.strAllocationUnits == "MB") // found in MS docs
472 || (i.strAllocationUnits == "byte * 2^20") // suggested by OVF spec DSP0243 page 21
473 )
474 vsys.ullMemorySize = i.ullVirtualQuantity * 1024 * 1024;
475 else
476 throw OVFLogicError(N_("Error reading \"%s\": Invalid allocation unit \"%s\" specified with memory size item, line %d"),
477 m_strPath.c_str(),
478 i.strAllocationUnits.c_str(),
479 i.ulLineNumber);
480 break;
481
482 case ResourceType_IDEController: // 5
483 {
484 /* <Item>
485 <rasd:Caption>ideController0</rasd:Caption>
486 <rasd:Description>IDE Controller</rasd:Description>
487 <rasd:InstanceId>5</rasd:InstanceId>
488 <rasd:ResourceType>5</rasd:ResourceType>
489 <rasd:Address>0</rasd:Address>
490 <rasd:BusNumber>0</rasd:BusNumber>
491 </Item> */
492 HardDiskController hdc;
493 hdc.system = HardDiskController::IDE;
494 hdc.idController = i.ulInstanceID;
495 hdc.strControllerType = i.strResourceSubType;
496
497 // if there is a numeric address tag for the IDE controller, use that;
498 // VMware uses "0" and "1" to keep the two OVF IDE controllers apart;
499 // otherwise use the "bus number" field which was specified in some old
500 // OVF files (but not the standard)
501 if (i.strAddress == "0")
502 hdc.ulAddress = 0;
503 else if (i.strAddress == "1")
504 hdc.ulAddress = 1;
505 else if (i.strAddress == "2") // just to be sure, this doesn't seem to be used by VMware
506 hdc.ulAddress = 2;
507 else if (i.strAddress == "3")
508 hdc.ulAddress = 3;
509 else
510 hdc.ulAddress = i.ulBusNumber;
511
512 vsys.mapControllers[i.ulInstanceID] = hdc;
513 }
514 break;
515
516 case ResourceType_ParallelSCSIHBA: // 6 SCSI controller
517 {
518 /* <Item>
519 <rasd:Caption>SCSI Controller 0 - LSI Logic</rasd:Caption>
520 <rasd:Description>SCI Controller</rasd:Description>
521 <rasd:ElementName>SCSI controller</rasd:ElementName>
522 <rasd:InstanceID>4</rasd:InstanceID>
523 <rasd:ResourceSubType>LsiLogic</rasd:ResourceSubType>
524 <rasd:ResourceType>6</rasd:ResourceType>
525 </Item> */
526 HardDiskController hdc;
527 hdc.system = HardDiskController::SCSI;
528 hdc.idController = i.ulInstanceID;
529 hdc.strControllerType = i.strResourceSubType;
530
531 vsys.mapControllers[i.ulInstanceID] = hdc;
532 }
533 break;
534
535 case ResourceType_EthernetAdapter: // 10
536 {
537 /* <Item>
538 <rasd:Caption>Ethernet adapter on 'Bridged'</rasd:Caption>
539 <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
540 <rasd:Connection>Bridged</rasd:Connection>
541 <rasd:InstanceID>6</rasd:InstanceID>
542 <rasd:ResourceType>10</rasd:ResourceType>
543 <rasd:ResourceSubType>E1000</rasd:ResourceSubType>
544 </Item>
545
546 OVF spec DSP 0243 page 21:
547 "For an Ethernet adapter, this specifies the abstract network connection name
548 for the virtual machine. All Ethernet adapters that specify the same abstract
549 network connection name within an OVF package shall be deployed on the same
550 network. The abstract network connection name shall be listed in the NetworkSection
551 at the outermost envelope level." */
552
553 // only store the name
554 EthernetAdapter ea;
555 ea.strAdapterType = i.strResourceSubType;
556 ea.strNetworkName = i.strConnection;
557 vsys.llEthernetAdapters.push_back(ea);
558 }
559 break;
560
561 case ResourceType_FloppyDrive: // 14
562 vsys.fHasFloppyDrive = true; // we have no additional information
563 break;
564
565 case ResourceType_CDDrive: // 15
566 /* <Item ovf:required="false">
567 <rasd:Caption>cdrom1</rasd:Caption>
568 <rasd:InstanceId>7</rasd:InstanceId>
569 <rasd:ResourceType>15</rasd:ResourceType>
570 <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
571 <rasd:Parent>5</rasd:Parent>
572 <rasd:AddressOnParent>0</rasd:AddressOnParent>
573 </Item> */
574 // I tried to see what happens if I set an ISO for the CD-ROM in VMware Workstation,
575 // but then the ovftool dies with "Device backing not supported". So I guess if
576 // VMware can't export ISOs, then we don't need to be able to import them right now.
577 vsys.fHasCdromDrive = true; // we have no additional information
578 break;
579
580 case ResourceType_HardDisk: // 17
581 // handled separately in second loop below
582 break;
583
584 case ResourceType_OtherStorageDevice: // 20 SATA controller
585 {
586 /* <Item>
587 <rasd:Description>SATA Controller</rasd:Description>
588 <rasd:Caption>sataController0</rasd:Caption>
589 <rasd:InstanceID>4</rasd:InstanceID>
590 <rasd:ResourceType>20</rasd:ResourceType>
591 <rasd:ResourceSubType>AHCI</rasd:ResourceSubType>
592 <rasd:Address>0</rasd:Address>
593 <rasd:BusNumber>0</rasd:BusNumber>
594 </Item> */
595 if ( i.strCaption.startsWith("sataController", MiniString::CaseInsensitive)
596 && !i.strResourceSubType.compare("AHCI", MiniString::CaseInsensitive)
597 )
598 {
599 HardDiskController hdc;
600 hdc.system = HardDiskController::SATA;
601 hdc.idController = i.ulInstanceID;
602 hdc.strControllerType = i.strResourceSubType;
603
604 vsys.mapControllers[i.ulInstanceID] = hdc;
605 }
606 else
607 throw OVFLogicError(N_("Error reading \"%s\": Host resource of type \"Other Storage Device (%d)\" is supported with SATA AHCI controllers only, line %d"),
608 m_strPath.c_str(),
609 ResourceType_OtherStorageDevice,
610 i.ulLineNumber);
611 }
612 break;
613
614 case ResourceType_USBController: // 23
615 /* <Item ovf:required="false">
616 <rasd:Caption>usb</rasd:Caption>
617 <rasd:Description>USB Controller</rasd:Description>
618 <rasd:InstanceId>3</rasd:InstanceId>
619 <rasd:ResourceType>23</rasd:ResourceType>
620 <rasd:Address>0</rasd:Address>
621 <rasd:BusNumber>0</rasd:BusNumber>
622 </Item> */
623 vsys.fHasUsbController = true; // we have no additional information
624 break;
625
626 case ResourceType_SoundCard: // 35
627 /* <Item ovf:required="false">
628 <rasd:Caption>sound</rasd:Caption>
629 <rasd:Description>Sound Card</rasd:Description>
630 <rasd:InstanceId>10</rasd:InstanceId>
631 <rasd:ResourceType>35</rasd:ResourceType>
632 <rasd:ResourceSubType>ensoniq1371</rasd:ResourceSubType>
633 <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
634 <rasd:AddressOnParent>3</rasd:AddressOnParent>
635 </Item> */
636 vsys.strSoundCardType = i.strResourceSubType;
637 break;
638
639 default:
640 throw OVFLogicError(N_("Error reading \"%s\": Unknown resource type %d in hardware item, line %d"),
641 m_strPath.c_str(),
642 i.resourceType,
643 i.ulLineNumber);
644 } // end switch
645 }
646
647 // now run through the items for a second time, but handle only
648 // hard disk images; otherwise the code would fail if a hard
649 // disk image appears in the OVF before its hard disk controller
650 for (itH = vsys.mapHardwareItems.begin();
651 itH != vsys.mapHardwareItems.end();
652 ++itH)
653 {
654 const VirtualHardwareItem &i = itH->second;
655
656 // do some analysis
657 switch (i.resourceType)
658 {
659 case ResourceType_HardDisk: // 17
660 {
661 /* <Item>
662 <rasd:Caption>Harddisk 1</rasd:Caption>
663 <rasd:Description>HD</rasd:Description>
664 <rasd:ElementName>Hard Disk</rasd:ElementName>
665 <rasd:HostResource>ovf://disk/lamp</rasd:HostResource>
666 <rasd:InstanceID>5</rasd:InstanceID>
667 <rasd:Parent>4</rasd:Parent>
668 <rasd:ResourceType>17</rasd:ResourceType>
669 </Item> */
670
671 // look up the hard disk controller element whose InstanceID equals our Parent;
672 // this is how the connection is specified in OVF
673 ControllersMap::const_iterator it = vsys.mapControllers.find(i.ulParent);
674 if (it == vsys.mapControllers.end())
675 throw OVFLogicError(N_("Error reading \"%s\": Hard disk item with instance ID %d specifies invalid parent %d, line %d"),
676 m_strPath.c_str(),
677 i.ulInstanceID,
678 i.ulParent,
679 i.ulLineNumber);
680 //const HardDiskController &hdc = it->second;
681
682 VirtualDisk vd;
683 vd.idController = i.ulParent;
684 i.strAddressOnParent.toInt(vd.ulAddressOnParent);
685 // ovf://disk/lamp
686 // 123456789012345
687 if (i.strHostResource.substr(0, 11) == "ovf://disk/")
688 vd.strDiskId = i.strHostResource.substr(11);
689 else if (i.strHostResource.substr(0, 10) == "ovf:/disk/")
690 vd.strDiskId = i.strHostResource.substr(10);
691 else if (i.strHostResource.substr(0, 6) == "/disk/")
692 vd.strDiskId = i.strHostResource.substr(6);
693
694 if ( !(vd.strDiskId.length())
695 || (m_mapDisks.find(vd.strDiskId) == m_mapDisks.end())
696 )
697 throw OVFLogicError(N_("Error reading \"%s\": Hard disk item with instance ID %d specifies invalid host resource \"%s\", line %d"),
698 m_strPath.c_str(),
699 i.ulInstanceID,
700 i.strHostResource.c_str(),
701 i.ulLineNumber);
702
703 vsys.mapVirtualDisks[vd.strDiskId] = vd;
704 }
705 break;
706 default: break;
707 }
708 }
709 }
710 else if ( (!strcmp(pcszElemName, "OperatingSystemSection"))
711 || (!strcmp(pcszTypeAttr, "ovf:OperatingSystemSection_Type"))
712 )
713 {
714 uint64_t cimos64;
715 if (!(pelmThis->getAttributeValue("id", cimos64)))
716 throw OVFLogicError(N_("Error reading \"%s\": missing or invalid 'ovf:id' attribute in operating system section element, line %d"),
717 m_strPath.c_str(),
718 pelmThis->getLineNumber());
719
720 vsys.cimos = (CIMOSType_T)cimos64;
721 const xml::ElementNode *pelmCIMOSDescription;
722 if ((pelmCIMOSDescription = pelmThis->findChildElement("Description")))
723 vsys.strCimosDesc = pelmCIMOSDescription->getValue();
724 }
725 else if ( (!strcmp(pcszElemName, "AnnotationSection"))
726 || (!strcmp(pcszTypeAttr, "ovf:AnnotationSection_Type"))
727 )
728 {
729 const xml::ElementNode *pelmAnnotation;
730 if ((pelmAnnotation = pelmThis->findChildElement("Annotation")))
731 vsys.strDescription = pelmAnnotation->getValue();
732 }
733 }
734
735 // now create the virtual system
736 m_llVirtualSystems.push_back(vsys);
737}
738
739////////////////////////////////////////////////////////////////////////////////
740//
741// Errors
742//
743////////////////////////////////////////////////////////////////////////////////
744
745OVFLogicError::OVFLogicError(const char *aFormat, ...)
746{
747 char *pszNewMsg;
748 va_list args;
749 va_start(args, aFormat);
750 RTStrAPrintfV(&pszNewMsg, aFormat, args);
751 setWhat(pszNewMsg);
752 RTStrFree(pszNewMsg);
753 va_end(args);
754}
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