VirtualBox

source: vbox/trunk/src/VBox/Main/ApplianceImpl.cpp@ 16926

Last change on this file since 16926 was 16926, checked in by vboxsync, 16 years ago

OVF: fix session handling (code failed if machine had more than one disk image attached); fix broken error reporting (null string passed with %d)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 101.9 KB
Line 
1/* $Id: ApplianceImpl.cpp 16926 2009-02-18 17:26:40Z vboxsync $ */
2/** @file
3 *
4 * IAppliance and IVirtualSystem COM class implementations
5 */
6
7/*
8 * Copyright (C) 2008-2009 Sun Microsystems, Inc.
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
19 * Clara, CA 95054 USA or visit http://www.sun.com if you need
20 * additional information or have any questions.
21 */
22
23#include <iprt/stream.h>
24#include <iprt/path.h>
25#include <iprt/dir.h>
26#include <iprt/file.h>
27
28#include "ApplianceImpl.h"
29#include "VirtualBoxImpl.h"
30#include "GuestOSTypeImpl.h"
31#include "ProgressImpl.h"
32
33#include "Logging.h"
34
35#include "VBox/xml.h"
36
37using namespace std;
38
39// defines
40////////////////////////////////////////////////////////////////////////////////
41
42struct DiskImage
43{
44 Utf8Str strDiskId; // value from DiskSection/Disk/@diskId
45 int64_t iCapacity; // value from DiskSection/Disk/@capacity;
46 // (maximum size for dynamic images, I guess; we always translate this to bytes)
47 int64_t iPopulatedSize; // value from DiskSection/Disk/@populatedSize
48 // (actual used size of disk, always in bytes; can be an estimate of used disk
49 // space, but cannot be larger than iCapacity)
50 Utf8Str strFormat; // value from DiskSection/Disk/@format
51 // typically http://www.vmware.com/specifications/vmdk.html#sparse
52
53 // fields from /References/File; the spec says the file reference from disk can be empty,
54 // so in that case, strFilename will be empty, then a new disk should be created
55 Utf8Str strHref; // value from /References/File/@href (filename); if empty, then the remaining fields are ignored
56 int64_t iSize; // value from /References/File/@size (optional according to spec; then we set -1 here)
57 int64_t iChunkSize; // value from /References/File/@chunkSize (optional, unsupported)
58 Utf8Str strCompression; // value from /References/File/@compression (optional, can be "gzip" according to spec)
59};
60
61struct Network
62{
63 Utf8Str strNetworkName; // value from NetworkSection/Network/@name
64 // unfortunately the OVF spec is unspecific about how networks should be specified further
65};
66
67struct VirtualHardwareItem
68{
69 Utf8Str strDescription;
70 Utf8Str strCaption;
71 Utf8Str strElementName;
72
73 uint32_t ulInstanceID;
74 uint32_t ulParent;
75
76 OVFResourceType_T resourceType;
77 Utf8Str strOtherResourceType;
78 Utf8Str strResourceSubType;
79
80 Utf8Str strHostResource; // "Abstractly specifies how a device shall connect to a resource on the deployment platform.
81 // Not all devices need a backing." Used with disk items, for which this references a virtual
82 // disk from the Disks section.
83 bool fAutomaticAllocation;
84 bool fAutomaticDeallocation;
85 Utf8Str strConnection; // "All Ethernet adapters that specify the same abstract network connection name within an OVF
86 // package shall be deployed on the same network. The abstract network connection name shall be
87 // listed in the NetworkSection at the outermost envelope level."
88 Utf8Str strAddress; // "Device-specific. For an Ethernet adapter, this specifies the MAC address."
89 Utf8Str strAddressOnParent; // "For a device, this specifies its location on the controller."
90 Utf8Str strAllocationUnits; // "Specifies the units of allocation used. For example, “byte * 2^20”."
91 uint64_t ullVirtualQuantity; // "Specifies the quantity of resources presented. For example, “256”."
92 uint64_t ullReservation; // "Specifies the minimum quantity of resources guaranteed to be available."
93 uint64_t ullLimit; // "Specifies the maximum quantity of resources that will be granted."
94 uint64_t ullWeight; // "Specifies a relative priority for this allocation in relation to other allocations."
95
96 Utf8Str strConsumerVisibility;
97 Utf8Str strMappingBehavior;
98 Utf8Str strPoolID;
99 uint32_t ulBusNumber; // seen with IDE controllers, but not listed in OVF spec
100
101 uint32_t ulLineNumber; // line number of <Item> element in XML source; cached for error messages
102
103 VirtualHardwareItem()
104 : ulInstanceID(0), fAutomaticAllocation(false), fAutomaticDeallocation(false), ullVirtualQuantity(0), ullReservation(0), ullLimit(0), ullWeight(0), ulBusNumber(0), ulLineNumber(0)
105 {};
106};
107
108typedef map<Utf8Str, DiskImage> DiskImagesMap;
109typedef map<Utf8Str, Network> NetworksMap;
110
111struct VirtualSystem;
112
113// opaque private instance data of Appliance class
114struct Appliance::Data
115{
116 Bstr bstrPath;
117
118 DiskImagesMap mapDisks; // map of DiskImage structs, sorted by DiskImage.strDiskId
119
120 NetworksMap mapNetworks; // map of Network structs, sorted by Network.strNetworkName
121
122 list<VirtualSystem> llVirtualSystems;
123
124 list< ComObjPtr<VirtualSystemDescription> > virtualSystemDescriptions;
125};
126
127typedef map<uint32_t, VirtualHardwareItem> HardwareItemsMap;
128
129struct HardDiskController
130{
131 uint32_t idController; // instance ID (Item/InstanceId); this gets referenced from HardDisk
132 enum ControllerSystemType { IDE, SATA, SCSI };
133 ControllerSystemType system; // one of IDE, SATA, SCSI
134 Utf8Str strControllerType; // controller subtype (Item/ResourceSubType); e.g. "LsiLogic"; can be empty (esp. for IDE)
135 Utf8Str strAddress; // for IDE
136 uint32_t ulBusNumber; // for IDE
137
138 HardDiskController()
139 : idController(0),
140 ulBusNumber(0)
141 {
142 }
143};
144
145typedef map<uint32_t, HardDiskController> ControllersMap;
146
147struct VirtualDisk
148{
149 uint32_t idController; // SCSI (or IDE) controller this disk is connected to;
150 // points into VirtualSystem.mapControllers
151 Utf8Str strDiskId; // if the hard disk has an ovf:/disk/<id> reference,
152 // this receives the <id> component; points to one of the
153 // references in Appliance::Data.mapDisks
154};
155
156typedef map<Utf8Str, VirtualDisk> VirtualDisksMap;
157
158struct VirtualSystem
159{
160 Utf8Str strName; // copy of VirtualSystem/@id
161
162 CIMOSType_T cimos;
163 Utf8Str strVirtualSystemType; // generic hardware description; OVF says this can be something like "vmx-4" or "xen";
164 // VMware Workstation 6.5 is "vmx-07"
165
166 HardwareItemsMap mapHardwareItems; // map of virtual hardware items, sorted by unique instance ID
167
168 uint64_t ullMemorySize; // always in bytes, copied from llHardwareItems; default = 0 (unspecified)
169 uint16_t cCPUs; // no. of CPUs, copied from llHardwareItems; default = 1
170
171 list<Utf8Str> llNetworkNames;
172 // list of strings referring to network names
173 // (one for each VirtualSystem/Item[@ResourceType=10]/Connection element)
174
175 ControllersMap mapControllers;
176 // list of hard disk controllers
177 // (one for each VirtualSystem/Item[@ResourceType=6] element with accumulated data from children)
178
179 VirtualDisksMap mapVirtualDisks;
180 // (one for each VirtualSystem/Item[@ResourceType=17] element with accumulated data from children)
181
182 bool fHasFloppyDrive; // true if there's a floppy item in mapHardwareItems
183 bool fHasCdromDrive; // true if there's a CD-ROM item in mapHardwareItems; ISO images are not yet supported by OVFtool
184 bool fHasUsbController; // true if there's a USB controller item in mapHardwareItems
185
186 Utf8Str strSoundCardType; // if not empty, then the system wants a soundcard; this then specifies the hardware;
187 // VMware Workstation 6.5 uses "ensoniq1371" for example
188
189 Utf8Str strLicenceInfo; // license info if any; receives contents of VirtualSystem/EulaSection/Info
190 Utf8Str strLicenceText; // license info if any; receives contents of VirtualSystem/EulaSection/License
191
192 VirtualSystem()
193 : ullMemorySize(0), cCPUs(1), fHasFloppyDrive(false), fHasCdromDrive(false), fHasUsbController(false)
194 {
195 }
196};
197
198struct Appliance::Task
199{
200 Task(Appliance *aThat, Progress *aProgress)
201 : that(aThat)
202 , progress(aProgress)
203 , rc(S_OK)
204 {}
205 ~Task() {}
206
207 HRESULT startThread();
208
209 Appliance *that;
210 ComObjPtr<Progress> progress;
211 HRESULT rc;
212};
213
214// globals
215////////////////////////////////////////////////////////////////////////////////
216
217static Utf8Str stripFilename(const Utf8Str &strFile)
218{
219 Utf8Str str2(strFile);
220 RTPathStripFilename(str2.mutableRaw());
221 return str2;
222}
223
224// IVirtualBox public methods
225////////////////////////////////////////////////////////////////////////////////
226
227/**
228 * Implementation for IVirtualBox::createAppliance.
229 *
230 * @param anAppliance IAppliance object created if S_OK is returned.
231 * @return S_OK or error.
232 */
233STDMETHODIMP VirtualBox::CreateAppliance(IAppliance** anAppliance)
234{
235 HRESULT rc;
236
237 ComObjPtr<Appliance> appliance;
238 appliance.createObject();
239 rc = appliance->init(this);
240
241 if (SUCCEEDED(rc))
242 appliance.queryInterfaceTo(anAppliance);
243
244 return rc;
245}
246
247// Appliance::task methods
248////////////////////////////////////////////////////////////////////////////////
249
250HRESULT Appliance::Task::startThread()
251{
252 int vrc = RTThreadCreate(NULL, Appliance::taskThread, this,
253 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
254 "Appliance::Task");
255 ComAssertMsgRCRet(vrc,
256 ("Could not create Appliance::Task thread (%Rrc)\n", vrc), E_FAIL);
257
258 return S_OK;
259}
260
261// IAppliance constructor / destructor
262////////////////////////////////////////////////////////////////////////////////
263
264DEFINE_EMPTY_CTOR_DTOR(Appliance)
265struct shutup {};
266
267// IAppliance private methods
268////////////////////////////////////////////////////////////////////////////////
269
270/**
271 * Private helper method that goes thru the elements of the given "current" element in the OVF XML
272 * and handles the contained child elements (which can be "Section" or "Content" elements).
273 *
274 * @param pcszPath Path spec of the XML file, for error messages.
275 * @param pReferencesElement "References" element from OVF, for looking up file specifications; can be NULL if no such element is present.
276 * @param pCurElem Element whose children are to be analyzed here.
277 * @return
278 */
279HRESULT Appliance::LoopThruSections(const char *pcszPath,
280 const xml::Node *pReferencesElem,
281 const xml::Node *pCurElem)
282{
283 HRESULT rc;
284
285 xml::NodesLoop loopChildren(*pCurElem);
286 const xml::Node *pElem;
287 while ((pElem = loopChildren.forAllNodes()))
288 {
289 const char *pcszElemName = pElem->getName();
290 const char *pcszTypeAttr = "";
291 const xml::Node *pTypeAttr;
292 if ((pTypeAttr = pElem->findAttribute("type")))
293 pcszTypeAttr = pTypeAttr->getValue();
294
295 if ( (!strcmp(pcszElemName, "DiskSection"))
296 || ( (!strcmp(pcszElemName, "Section"))
297 && (!strcmp(pcszTypeAttr, "ovf:DiskSection_Type"))
298 )
299 )
300 {
301 if (!(SUCCEEDED((rc = HandleDiskSection(pcszPath, pReferencesElem, pElem)))))
302 return rc;
303 }
304 else if ( (!strcmp(pcszElemName, "NetworkSection"))
305 || ( (!strcmp(pcszElemName, "Section"))
306 && (!strcmp(pcszTypeAttr, "ovf:NetworkSection_Type"))
307 )
308 )
309 {
310 if (!(SUCCEEDED((rc = HandleNetworkSection(pcszPath, pElem)))))
311 return rc;
312 }
313 else if ( (!strcmp(pcszElemName, "DeploymentOptionSection>")))
314 {
315 // TODO
316 }
317 else if ( (!strcmp(pcszElemName, "Info")))
318 {
319 // child of VirtualSystemCollection -- TODO
320 }
321 else if ( (!strcmp(pcszElemName, "ResourceAllocationSection")))
322 {
323 // child of VirtualSystemCollection -- TODO
324 }
325 else if ( (!strcmp(pcszElemName, "StartupSection")))
326 {
327 // child of VirtualSystemCollection -- TODO
328 }
329 else if ( (!strcmp(pcszElemName, "VirtualSystem"))
330 || ( (!strcmp(pcszElemName, "Content"))
331 && (!strcmp(pcszTypeAttr, "ovf:VirtualSystem_Type"))
332 )
333 )
334 {
335 if (!(SUCCEEDED((rc = HandleVirtualSystemContent(pcszPath, pElem)))))
336 return rc;
337 }
338 else if ( (!strcmp(pcszElemName, "VirtualSystemCollection"))
339 || ( (!strcmp(pcszElemName, "Content"))
340 && (!strcmp(pcszTypeAttr, "ovf:VirtualSystemCollection_Type"))
341 )
342 )
343 {
344 // TODO ResourceAllocationSection
345
346 // recurse for this, since it has VirtualSystem elements as children
347 if (!(SUCCEEDED((rc = LoopThruSections(pcszPath, pReferencesElem, pElem)))))
348 return rc;
349 }
350 }
351
352 return S_OK;
353}
354
355/**
356 * Private helper method that handles disk sections in the OVF XML.
357 * @param pcszPath Path spec of the XML file, for error messages.
358 * @param pReferencesElement "References" element from OVF, for looking up file specifications; can be NULL if no such element is present.
359 * @param pSectionElem Section element for which this helper is getting called.
360 * @return
361 */
362HRESULT Appliance::HandleDiskSection(const char *pcszPath,
363 const xml::Node *pReferencesElem,
364 const xml::Node *pSectionElem)
365{
366 // contains "Disk" child elements
367 xml::NodesLoop loopDisks(*pSectionElem, "Disk");
368 const xml::Node *pelmDisk;
369 while ((pelmDisk = loopDisks.forAllNodes()))
370 {
371 DiskImage d;
372 const char *pcszBad = NULL;
373 if (!(pelmDisk->getAttributeValue("diskId", d.strDiskId)))
374 pcszBad = "diskId";
375 else if (!(pelmDisk->getAttributeValue("format", d.strFormat)))
376 pcszBad = "format";
377 else if (!(pelmDisk->getAttributeValue("capacity", d.iCapacity)))
378 pcszBad = "capacity";
379 else
380 {
381 if (!(pelmDisk->getAttributeValue("populatedSize", d.iPopulatedSize)))
382 // optional
383 d.iPopulatedSize = -1;
384
385 Utf8Str strFileRef;
386 if (pelmDisk->getAttributeValue("fileRef", strFileRef)) // optional
387 {
388 // look up corresponding /References/File nodes (list built above)
389 const xml::Node *pFileElem;
390 if ( pReferencesElem
391 && ((pFileElem = pReferencesElem->findChildElementFromId(strFileRef.c_str())))
392 )
393 {
394 // copy remaining values from file node then
395 const char *pcszBadInFile = NULL;
396 if (!(pFileElem->getAttributeValue("href", d.strHref)))
397 pcszBadInFile = "href";
398 else if (!(pFileElem->getAttributeValue("size", d.iSize)))
399 d.iSize = -1; // optional
400 // if (!(pFileElem->getAttributeValue("size", d.iChunkSize))) TODO
401 d.iChunkSize = -1; // optional
402 pFileElem->getAttributeValue("compression", d.strCompression);
403
404 if (pcszBadInFile)
405 return setError(VBOX_E_FILE_ERROR,
406 tr("Error reading \"%s\": missing or invalid attribute '%s' in 'File' element, line %d"),
407 pcszPath,
408 pcszBadInFile,
409 pFileElem->getLineNumber());
410 }
411 else
412 return setError(VBOX_E_FILE_ERROR,
413 tr("Error reading \"%s\": cannot find References/File element for ID '%s' referenced by 'Disk' element, line %d"),
414 pcszPath,
415 strFileRef.c_str(),
416 pelmDisk->getLineNumber());
417 }
418 }
419
420 if (pcszBad)
421 return setError(VBOX_E_FILE_ERROR,
422 tr("Error reading \"%s\": missing or invalid attribute '%s' in 'DiskSection' element, line %d"),
423 pcszPath,
424 pcszBad,
425 pelmDisk->getLineNumber());
426
427 m->mapDisks[d.strDiskId] = d;
428 }
429
430 return S_OK;
431}
432
433/**
434 * Private helper method that handles network sections in the OVF XML.
435 * @param pcszPath Path spec of the XML file, for error messages.
436 * @param pSectionElem Section element for which this helper is getting called.
437 * @return
438 */
439HRESULT Appliance::HandleNetworkSection(const char *pcszPath,
440 const xml::Node *pSectionElem)
441{
442 // contains "Disk" child elements
443 xml::NodesLoop loopNetworks(*pSectionElem, "Network");
444 const xml::Node *pelmNetwork;
445 while ((pelmNetwork = loopNetworks.forAllNodes()))
446 {
447 Network n;
448 if (!(pelmNetwork->getAttributeValue("name", n.strNetworkName)))
449 return setError(VBOX_E_FILE_ERROR,
450 tr("Error reading \"%s\": missing 'name' attribute in 'Network', line %d"),
451 pcszPath,
452 pelmNetwork->getLineNumber());
453
454 m->mapNetworks[n.strNetworkName] = n;
455 }
456
457 return S_OK;
458}
459
460/**
461 * Private helper method that handles a "VirtualSystem" element in the OVF XML.
462 *
463 * @param pcszPath
464 * @param pContentElem
465 * @return
466 */
467HRESULT Appliance::HandleVirtualSystemContent(const char *pcszPath,
468 const xml::Node *pelmVirtualSystem)
469{
470 VirtualSystem vsys;
471
472 const xml::Node *pIdAttr = pelmVirtualSystem->findAttribute("id");
473 if (pIdAttr)
474 vsys.strName = pIdAttr->getValue();
475
476 xml::NodesLoop loop(*pelmVirtualSystem); // all child elements
477 const xml::Node *pelmThis;
478 while ((pelmThis = loop.forAllNodes()))
479 {
480 const char *pcszElemName = pelmThis->getName();
481 const xml::Node *pTypeAttr = pelmThis->findAttribute("type");
482 const char *pcszTypeAttr = (pTypeAttr) ? pTypeAttr->getValue() : "";
483
484 if (!strcmp(pcszElemName, "EulaSection"))
485 {
486 /* <EulaSection>
487 <Info ovf:msgid="6">License agreement for the Virtual System.</Info>
488 <License ovf:msgid="1">License terms can go in here.</License>
489 </EulaSection> */
490
491 const xml::Node *pelmInfo, *pelmLicense;
492 if ( ((pelmInfo = pelmThis->findChildElement("Info")))
493 && ((pelmLicense = pelmThis->findChildElement("License")))
494 )
495 {
496 vsys.strLicenceInfo = pelmInfo->getValue();
497 vsys.strLicenceText = pelmLicense->getValue();
498 }
499 }
500 else if ( (!strcmp(pcszElemName, "VirtualHardwareSection"))
501 || (!strcmp(pcszTypeAttr, "ovf:VirtualHardwareSection_Type"))
502 )
503 {
504 const xml::Node *pelmSystem, *pelmVirtualSystemType;
505 if ((pelmSystem = pelmThis->findChildElement("System")))
506 {
507 /* <System>
508 <vssd:Description>Description of the virtual hardware section.</vssd:Description>
509 <vssd:ElementName>vmware</vssd:ElementName>
510 <vssd:InstanceID>1</vssd:InstanceID>
511 <vssd:VirtualSystemIdentifier>MyLampService</vssd:VirtualSystemIdentifier>
512 <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType>
513 </System>*/
514 if ((pelmVirtualSystemType = pelmSystem->findChildElement("VirtualSystemType")))
515 vsys.strVirtualSystemType = pelmVirtualSystemType->getValue();
516 }
517
518 xml::NodesLoop loopVirtualHardwareItems(*pelmThis, "Item"); // all "Item" child elements
519 const xml::Node *pelmItem;
520 while ((pelmItem = loopVirtualHardwareItems.forAllNodes()))
521 {
522 VirtualHardwareItem i;
523
524 i.ulLineNumber = pelmItem->getLineNumber();
525
526 xml::NodesLoop loopItemChildren(*pelmItem); // all child elements
527 const xml::Node *pelmItemChild;
528 while ((pelmItemChild = loopItemChildren.forAllNodes()))
529 {
530 const char *pcszItemChildName = pelmItemChild->getName();
531 if (!strcmp(pcszItemChildName, "Description"))
532 i.strDescription = pelmItemChild->getValue();
533 else if (!strcmp(pcszItemChildName, "Caption"))
534 i.strCaption = pelmItemChild->getValue();
535 else if (!strcmp(pcszItemChildName, "ElementName"))
536 i.strElementName = pelmItemChild->getValue();
537 else if ( (!strcmp(pcszItemChildName, "InstanceID"))
538 || (!strcmp(pcszItemChildName, "InstanceId"))
539 )
540 pelmItemChild->copyValue(i.ulInstanceID);
541 else if (!strcmp(pcszItemChildName, "HostResource"))
542 i.strHostResource = pelmItemChild->getValue();
543 else if (!strcmp(pcszItemChildName, "ResourceType"))
544 {
545 int32_t iType; /** @todo how to fix correctly? (enum fun.) */
546 pelmItemChild->copyValue(iType);
547 i.resourceType = (OVFResourceType_T)iType;
548 }
549 else if (!strcmp(pcszItemChildName, "OtherResourceType"))
550 i.strOtherResourceType = pelmItemChild->getValue();
551 else if (!strcmp(pcszItemChildName, "ResourceSubType"))
552 i.strResourceSubType = pelmItemChild->getValue();
553 else if (!strcmp(pcszItemChildName, "AutomaticAllocation"))
554 i.fAutomaticAllocation = (!strcmp(pelmItemChild->getValue(), "true")) ? true : false;
555 else if (!strcmp(pcszItemChildName, "AutomaticDeallocation"))
556 i.fAutomaticDeallocation = (!strcmp(pelmItemChild->getValue(), "true")) ? true : false;
557 else if (!strcmp(pcszItemChildName, "Parent"))
558 pelmItemChild->copyValue(i.ulParent);
559 else if (!strcmp(pcszItemChildName, "Connection"))
560 i.strConnection = pelmItemChild->getValue();
561 else if (!strcmp(pcszItemChildName, "Address"))
562 i.strAddress = pelmItemChild->getValue();
563 else if (!strcmp(pcszItemChildName, "AddressOnParent"))
564 i.strAddressOnParent = pelmItemChild->getValue();
565 else if (!strcmp(pcszItemChildName, "AllocationUnits"))
566 i.strAllocationUnits = pelmItemChild->getValue();
567 else if (!strcmp(pcszItemChildName, "VirtualQuantity"))
568 pelmItemChild->copyValue(i.ullVirtualQuantity);
569 else if (!strcmp(pcszItemChildName, "Reservation"))
570 pelmItemChild->copyValue(i.ullReservation);
571 else if (!strcmp(pcszItemChildName, "Limit"))
572 pelmItemChild->copyValue(i.ullLimit);
573 else if (!strcmp(pcszItemChildName, "Weight"))
574 pelmItemChild->copyValue(i.ullWeight);
575 else if (!strcmp(pcszItemChildName, "ConsumerVisibility"))
576 i.strConsumerVisibility = pelmItemChild->getValue();
577 else if (!strcmp(pcszItemChildName, "MappingBehavior"))
578 i.strMappingBehavior = pelmItemChild->getValue();
579 else if (!strcmp(pcszItemChildName, "PoolID"))
580 i.strPoolID = pelmItemChild->getValue();
581 else if (!strcmp(pcszItemChildName, "BusNumber"))
582 pelmItemChild->copyValue(i.ulBusNumber);
583 else
584 return setError(VBOX_E_FILE_ERROR,
585 tr("Error reading \"%s\": unknown element \"%s\" under Item element, line %d"),
586 pcszPath,
587 pcszItemChildName,
588 i.ulLineNumber);
589 }
590
591 // store!
592 vsys.mapHardwareItems[i.ulInstanceID] = i;
593 }
594
595 HardwareItemsMap::const_iterator itH;
596
597 for (itH = vsys.mapHardwareItems.begin();
598 itH != vsys.mapHardwareItems.end();
599 ++itH)
600 {
601 const VirtualHardwareItem &i = itH->second;
602
603 // do some analysis
604 switch (i.resourceType)
605 {
606 case OVFResourceType_Processor: // 3
607 /* <rasd:Caption>1 virtual CPU</rasd:Caption>
608 <rasd:Description>Number of virtual CPUs</rasd:Description>
609 <rasd:ElementName>virtual CPU</rasd:ElementName>
610 <rasd:InstanceID>1</rasd:InstanceID>
611 <rasd:ResourceType>3</rasd:ResourceType>
612 <rasd:VirtualQuantity>1</rasd:VirtualQuantity>*/
613 if (i.ullVirtualQuantity < UINT16_MAX)
614 vsys.cCPUs = (uint16_t)i.ullVirtualQuantity;
615 else
616 return setError(VBOX_E_FILE_ERROR,
617 tr("Error reading \"%s\": CPU count %RI64 is larger than %d, line %d"),
618 pcszPath,
619 i.ullVirtualQuantity,
620 UINT16_MAX,
621 i.ulLineNumber);
622 break;
623
624 case OVFResourceType_Memory: // 4
625 if ( (i.strAllocationUnits == "MegaBytes") // found in OVF created by OVF toolkit
626 || (i.strAllocationUnits == "MB") // found in MS docs
627 || (i.strAllocationUnits == "byte * 2^20") // suggested by OVF spec DSP0243 page 21
628 )
629 vsys.ullMemorySize = i.ullVirtualQuantity * 1024 * 1024;
630 else
631 return setError(VBOX_E_FILE_ERROR,
632 tr("Error reading \"%s\": Invalid allocation unit \"%s\" specified with memory size item, line %d"),
633 pcszPath,
634 i.strAllocationUnits.c_str(),
635 i.ulLineNumber);
636 break;
637
638 case OVFResourceType_IdeController: // 5 IdeController
639 {
640 /* <Item>
641 <rasd:Caption>ideController0</rasd:Caption>
642 <rasd:Description>IDE Controller</rasd:Description>
643 <rasd:InstanceId>5</rasd:InstanceId>
644 <rasd:ResourceType>5</rasd:ResourceType>
645 <rasd:Address>0</rasd:Address>
646 <rasd:BusNumber>0</rasd:BusNumber>
647 </Item> */
648 HardDiskController hdc;
649 hdc.system = HardDiskController::IDE;
650 hdc.idController = i.ulInstanceID;
651 hdc.strAddress = i.strAddress;
652 hdc.ulBusNumber = i.ulBusNumber;
653
654 vsys.mapControllers[i.ulInstanceID] = hdc;
655 }
656 break;
657
658 case OVFResourceType_ParallelScsiHba: // 6 SCSI controller
659 {
660 /* <Item>
661 <rasd:Caption>SCSI Controller 0 - LSI Logic</rasd:Caption>
662 <rasd:Description>SCI Controller</rasd:Description>
663 <rasd:ElementName>SCSI controller</rasd:ElementName>
664 <rasd:InstanceID>4</rasd:InstanceID>
665 <rasd:ResourceSubType>LsiLogic</rasd:ResourceSubType>
666 <rasd:ResourceType>6</rasd:ResourceType>
667 </Item> */
668 HardDiskController hdc;
669 hdc.system = HardDiskController::SCSI;
670 hdc.idController = i.ulInstanceID;
671 hdc.strControllerType = i.strResourceSubType;
672
673 vsys.mapControllers[i.ulInstanceID] = hdc;
674 }
675 break;
676
677 case OVFResourceType_EthernetAdapter: // 10
678 {
679 /* <Item>
680 <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
681 <rasd:Caption>Ethernet adapter on 'VM Network'</rasd:Caption>
682 <rasd:Connection>VM Network</rasd:Connection>
683 <rasd:Description>VM Network?</rasd:Description>
684 <rasd:ElementName>Ethernet adapter</rasd:ElementName>
685 <rasd:InstanceID>3</rasd:InstanceID>
686 <rasd:ResourceType>10</rasd:ResourceType>
687 </Item>
688
689 OVF spec DSP 0243 page 21:
690 "For an Ethernet adapter, this specifies the abstract network connection name
691 for the virtual machine. All Ethernet adapters that specify the same abstract
692 network connection name within an OVF package shall be deployed on the same
693 network. The abstract network connection name shall be listed in the NetworkSection
694 at the outermost envelope level." */
695
696 // make sure we have a matching NetworkSection/Network
697 NetworksMap::iterator it = m->mapNetworks.find(i.strConnection);
698 if (it == m->mapNetworks.end())
699 return setError(VBOX_E_FILE_ERROR,
700 tr("Error reading \"%s\": Invalid connection \"%s\"; cannot find matching NetworkSection/Network element, line %d"),
701 pcszPath,
702 i.strConnection.c_str(),
703 i.ulLineNumber);
704
705 vsys.llNetworkNames.push_back(i.strConnection);
706 }
707 break;
708
709 case OVFResourceType_FloppyDrive: // 14
710 vsys.fHasFloppyDrive = true; // we have no additional information
711 break;
712
713 case OVFResourceType_CdDrive: // 15
714 /* <Item ovf:required="false">
715 <rasd:Caption>cdrom1</rasd:Caption>
716 <rasd:InstanceId>7</rasd:InstanceId>
717 <rasd:ResourceType>15</rasd:ResourceType>
718 <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
719 <rasd:Parent>5</rasd:Parent>
720 <rasd:AddressOnParent>0</rasd:AddressOnParent>
721 </Item> */
722 // I tried to see what happens if I set an ISO for the CD-ROM in VMware Workstation,
723 // but then the ovftool dies with "Device backing not supported". So I guess if
724 // VMware can't export ISOs, then we don't need to be able to import them right now.
725 vsys.fHasCdromDrive = true; // we have no additional information
726 break;
727
728 case OVFResourceType_HardDisk: // 17
729 {
730 /* <Item>
731 <rasd:Caption>Harddisk 1</rasd:Caption>
732 <rasd:Description>HD</rasd:Description>
733 <rasd:ElementName>Hard Disk</rasd:ElementName>
734 <rasd:HostResource>ovf://disk/lamp</rasd:HostResource>
735 <rasd:InstanceID>5</rasd:InstanceID>
736 <rasd:Parent>4</rasd:Parent>
737 <rasd:ResourceType>17</rasd:ResourceType>
738 </Item> */
739
740 // look up the hard disk controller element whose InstanceID equals our Parent;
741 // this is how the connection is specified in OVF
742 ControllersMap::const_iterator it = vsys.mapControllers.find(i.ulParent);
743 if (it == vsys.mapControllers.end())
744 return setError(VBOX_E_FILE_ERROR,
745 tr("Error reading \"%s\": Hard disk item with instance ID %d specifies invalid parent %d, line %d"),
746 pcszPath,
747 i.ulInstanceID,
748 i.ulParent,
749 i.ulLineNumber);
750 const HardDiskController &hdc = it->second;
751
752 VirtualDisk vd;
753 vd.idController = i.ulParent;
754 bool fFound = false;
755 // ovf://disk/lamp
756 // 12345678901234
757 if (i.strHostResource.substr(0, 11) == "ovf://disk/")
758 vd.strDiskId = i.strHostResource.substr(11);
759 else if (i.strHostResource.substr(0, 6) == "/disk/")
760 vd.strDiskId = i.strHostResource.substr(6);
761
762 if ( !(vd.strDiskId.length())
763 || (m->mapDisks.find(vd.strDiskId) == m->mapDisks.end())
764 )
765 return setError(VBOX_E_FILE_ERROR,
766 tr("Error reading \"%s\": Hard disk item with instance ID %d specifies invalid host resource \"%s\", line %d"),
767 pcszPath,
768 i.ulInstanceID,
769 i.strHostResource.c_str(),
770 i.ulLineNumber);
771
772 vsys.mapVirtualDisks[vd.strDiskId] = vd;
773 }
774 break;
775
776 case OVFResourceType_UsbController: // 23
777 /* <Item ovf:required="false">
778 <rasd:Caption>usb</rasd:Caption>
779 <rasd:Description>USB Controller</rasd:Description>
780 <rasd:InstanceId>3</rasd:InstanceId>
781 <rasd:ResourceType>23</rasd:ResourceType>
782 <rasd:Address>0</rasd:Address>
783 <rasd:BusNumber>0</rasd:BusNumber>
784 </Item> */
785 vsys.fHasUsbController = true; // we have no additional information
786 break;
787
788 case OVFResourceType_SoundCard: // 35
789 /* <Item ovf:required="false">
790 <rasd:Caption>sound</rasd:Caption>
791 <rasd:Description>Sound Card</rasd:Description>
792 <rasd:InstanceId>10</rasd:InstanceId>
793 <rasd:ResourceType>35</rasd:ResourceType>
794 <rasd:ResourceSubType>ensoniq1371</rasd:ResourceSubType>
795 <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
796 <rasd:AddressOnParent>3</rasd:AddressOnParent>
797 </Item> */
798 vsys.strSoundCardType = i.strResourceSubType;
799 break;
800
801 default:
802 return setError(VBOX_E_FILE_ERROR,
803 tr("Error reading \"%s\": Unknown resource type %d in hardware item, line %d"),
804 pcszPath,
805 i.resourceType,
806 i.ulLineNumber);
807 }
808 }
809 }
810 else if ( (!strcmp(pcszElemName, "OperatingSystemSection"))
811 || (!strcmp(pcszTypeAttr, "ovf:OperatingSystemSection_Type"))
812 )
813 {
814 uint64_t cimos64;
815 if (!(pelmThis->getAttributeValue("id", cimos64)))
816 return setError(VBOX_E_FILE_ERROR,
817 tr("Error reading \"%s\": missing or invalid 'ovf:id' attribute in operating system section element, line %d"),
818 pcszPath,
819 pelmThis->getLineNumber());
820
821 vsys.cimos = (CIMOSType_T)cimos64;
822 }
823 }
824
825 // now create the virtual system
826 m->llVirtualSystems.push_back(vsys);
827
828 return S_OK;
829}
830
831// IAppliance public methods
832////////////////////////////////////////////////////////////////////////////////
833
834/**
835 * Appliance initializer.
836 *
837 * This loads the given appliance.
838 * @param
839 * @return
840 */
841
842HRESULT Appliance::init(VirtualBox *aVirtualBox)
843{
844 HRESULT rc;
845
846 /* Enclose the state transition NotReady->InInit->Ready */
847 AutoInitSpan autoInitSpan(this);
848 AssertReturn(autoInitSpan.isOk(), E_FAIL);
849
850 /* Weak reference to a VirtualBox object */
851 unconst(mVirtualBox) = aVirtualBox;
852
853 // initialize data
854 m = new Data;
855
856 /* Confirm a successful initialization */
857 autoInitSpan.setSucceeded();
858
859 return S_OK;
860}
861
862void Appliance::uninit()
863{
864 delete m;
865 m = NULL;
866}
867
868STDMETHODIMP Appliance::COMGETTER(Path)(BSTR *aPath)
869{
870 if (!aPath)
871 return E_POINTER;
872
873 AutoCaller autoCaller(this);
874 CheckComRCReturnRC(autoCaller.rc());
875
876 AutoReadLock alock(this);
877
878 m->bstrPath.cloneTo(aPath);
879
880 return S_OK;
881}
882
883STDMETHODIMP Appliance::COMGETTER(Disks)(ComSafeArrayOut(BSTR, aDisks))
884{
885 CheckComArgOutSafeArrayPointerValid(aDisks);
886
887 AutoCaller autoCaller(this);
888 CheckComRCReturnRC(autoCaller.rc());
889
890 AutoReadLock alock(this);
891
892 size_t c = m->mapDisks.size();
893 com::SafeArray<BSTR> sfaDisks(c);
894
895 DiskImagesMap::const_iterator it;
896 size_t i = 0;
897 for (it = m->mapDisks.begin();
898 it != m->mapDisks.end();
899 ++it, ++i)
900 {
901 // create a string representing this disk
902 const DiskImage &d = it->second;
903 char *psz = NULL;
904 RTStrAPrintf(&psz,
905 "%s\t"
906 "%RI64\t"
907 "%RI64\t"
908 "%s\t"
909 "%s\t"
910 "%RI64\t"
911 "%RI64\t"
912 "%s",
913 d.strDiskId.c_str(),
914 d.iCapacity,
915 d.iPopulatedSize,
916 d.strFormat.c_str(),
917 d.strHref.c_str(),
918 d.iSize,
919 d.iChunkSize,
920 d.strCompression.c_str());
921 Utf8Str utf(psz);
922 Bstr bstr(utf);
923 // push to safearray
924 bstr.cloneTo(&sfaDisks[i]);
925 RTStrFree(psz);
926 }
927
928 sfaDisks.detachTo(ComSafeArrayOutArg(aDisks));
929
930 return S_OK;
931}
932
933STDMETHODIMP Appliance::COMGETTER(VirtualSystemDescriptions)(ComSafeArrayOut(IVirtualSystemDescription*, aVirtualSystemDescriptions))
934{
935 CheckComArgOutSafeArrayPointerValid(aVirtualSystemDescriptions);
936
937 AutoCaller autoCaller(this);
938 CheckComRCReturnRC(autoCaller.rc());
939
940 AutoReadLock alock(this);
941
942 SafeIfaceArray<IVirtualSystemDescription> sfaVSD(m->virtualSystemDescriptions);
943 sfaVSD.detachTo(ComSafeArrayOutArg(aVirtualSystemDescriptions));
944
945 return S_OK;
946}
947
948void convertCIMOSType2VBoxOSType(Utf8Str &osTypeVBox, CIMOSType_T c)
949{
950 switch (c)
951 {
952 case CIMOSType_CIMOS_Unknown: // 0 - Unknown
953 osTypeVBox = SchemaDefs_OSTypeId_Other;
954 break;
955
956 case CIMOSType_CIMOS_OS2: // 12 - OS/2
957 osTypeVBox = SchemaDefs_OSTypeId_OS2;
958 break;
959
960 case CIMOSType_CIMOS_MSDOS: // 14 - MSDOS
961 osTypeVBox = SchemaDefs_OSTypeId_DOS;
962 break;
963
964 case CIMOSType_CIMOS_WIN3x: // 15 - WIN3x
965 osTypeVBox = SchemaDefs_OSTypeId_Windows31;
966 break;
967
968 case CIMOSType_CIMOS_WIN95: // 16 - WIN95
969 osTypeVBox = SchemaDefs_OSTypeId_Windows95;
970 break;
971
972 case CIMOSType_CIMOS_WIN98: // 17 - WIN98
973 osTypeVBox = SchemaDefs_OSTypeId_Windows98;
974 break;
975
976 case CIMOSType_CIMOS_WINNT: // 18 - WINNT
977 osTypeVBox = SchemaDefs_OSTypeId_WindowsNT4;
978 break;
979
980 case CIMOSType_CIMOS_NetWare: // 21 - NetWare
981 case CIMOSType_CIMOS_NovellOES: // 86 - Novell OES
982 osTypeVBox = SchemaDefs_OSTypeId_Netware;
983 break;
984
985 case CIMOSType_CIMOS_Solaris: // 29 - Solaris
986 case CIMOSType_CIMOS_SunOS: // 30 - SunOS
987 osTypeVBox = SchemaDefs_OSTypeId_Solaris;
988 break;
989
990 case CIMOSType_CIMOS_FreeBSD: // 42 - FreeBSD
991 osTypeVBox = SchemaDefs_OSTypeId_FreeBSD;
992 break;
993
994 case CIMOSType_CIMOS_NetBSD: // 43 - NetBSD
995 osTypeVBox = SchemaDefs_OSTypeId_NetBSD;
996 break;
997
998 case CIMOSType_CIMOS_QNX: // 48 - QNX
999 osTypeVBox = SchemaDefs_OSTypeId_QNX;
1000 break;
1001
1002 case CIMOSType_CIMOS_Windows2000: // 58 - Windows 2000
1003 osTypeVBox = SchemaDefs_OSTypeId_Windows2000;
1004 break;
1005
1006 case CIMOSType_CIMOS_WindowsMe: // 63 - Windows (R) Me
1007 osTypeVBox = SchemaDefs_OSTypeId_WindowsMe;
1008 break;
1009
1010 case CIMOSType_CIMOS_OpenBSD: // 65 - OpenBSD
1011 osTypeVBox = SchemaDefs_OSTypeId_OpenBSD;
1012 break;
1013
1014 case CIMOSType_CIMOS_WindowsXP: // 67 - Windows XP
1015 case CIMOSType_CIMOS_WindowsXPEmbedded: // 72 - Windows XP Embedded
1016 case CIMOSType_CIMOS_WindowsEmbeddedforPointofService: // 75 - Windows Embedded for Point of Service
1017 osTypeVBox = SchemaDefs_OSTypeId_WindowsXP;
1018 break;
1019
1020 case CIMOSType_CIMOS_MicrosoftWindowsServer2003: // 69 - Microsoft Windows Server 2003
1021 osTypeVBox = SchemaDefs_OSTypeId_Windows2003;
1022 break;
1023
1024 case CIMOSType_CIMOS_MicrosoftWindowsServer2003_64: // 70 - Microsoft Windows Server 2003 64-Bit
1025 osTypeVBox = SchemaDefs_OSTypeId_Windows2003_64;
1026 break;
1027
1028 case CIMOSType_CIMOS_WindowsXP_64: // 71 - Windows XP 64-Bit
1029 osTypeVBox = SchemaDefs_OSTypeId_WindowsXP_64;
1030 break;
1031
1032 case CIMOSType_CIMOS_WindowsVista: // 73 - Windows Vista
1033 osTypeVBox = SchemaDefs_OSTypeId_WindowsVista;
1034 break;
1035
1036 case CIMOSType_CIMOS_WindowsVista_64: // 74 - Windows Vista 64-Bit
1037 osTypeVBox = SchemaDefs_OSTypeId_WindowsVista_64;
1038 break;
1039
1040 case CIMOSType_CIMOS_MicrosoftWindowsServer2008: // 76 - Microsoft Windows Server 2008
1041 osTypeVBox = SchemaDefs_OSTypeId_Windows2008;
1042 break;
1043
1044 case CIMOSType_CIMOS_MicrosoftWindowsServer2008_64: // 77 - Microsoft Windows Server 2008 64-Bit
1045 osTypeVBox = SchemaDefs_OSTypeId_Windows2008_64;
1046 break;
1047
1048 case CIMOSType_CIMOS_FreeBSD_64: // 78 - FreeBSD 64-Bit
1049 osTypeVBox = SchemaDefs_OSTypeId_FreeBSD_64;
1050 break;
1051
1052 case CIMOSType_CIMOS_RedHatEnterpriseLinux: // 79 - RedHat Enterprise Linux
1053 osTypeVBox = SchemaDefs_OSTypeId_RedHat;
1054 break;
1055
1056 case CIMOSType_CIMOS_RedHatEnterpriseLinux_64: // 80 - RedHat Enterprise Linux 64-Bit
1057 osTypeVBox = SchemaDefs_OSTypeId_RedHat_64;
1058 break;
1059
1060 case CIMOSType_CIMOS_Solaris_64: // 81 - Solaris 64-Bit
1061 osTypeVBox = SchemaDefs_OSTypeId_Solaris_64;
1062 break;
1063
1064 case CIMOSType_CIMOS_SUSE: // 82 - SUSE
1065 case CIMOSType_CIMOS_SLES: // 84 - SLES
1066 case CIMOSType_CIMOS_NovellLinuxDesktop: // 87 - Novell Linux Desktop
1067 osTypeVBox = SchemaDefs_OSTypeId_OpenSUSE;
1068 break;
1069
1070 case CIMOSType_CIMOS_SUSE_64: // 83 - SUSE 64-Bit
1071 case CIMOSType_CIMOS_SLES_64: // 85 - SLES 64-Bit
1072 osTypeVBox = SchemaDefs_OSTypeId_OpenSUSE_64;
1073 break;
1074
1075 case CIMOSType_CIMOS_LINUX: // 36 - LINUX
1076 case CIMOSType_CIMOS_SunJavaDesktopSystem: // 88 - Sun Java Desktop System
1077 case CIMOSType_CIMOS_TurboLinux: // 91 - TurboLinux
1078 osTypeVBox = SchemaDefs_OSTypeId_Linux;
1079 break;
1080
1081 // case CIMOSType_CIMOS_TurboLinux_64: // 92 - TurboLinux 64-Bit
1082 // case CIMOSType_CIMOS_Linux_64: // 101 - Linux 64-Bit
1083 // osTypeVBox = VBOXOSTYPE_Linux_x64;
1084 // break;
1085
1086 case CIMOSType_CIMOS_Mandriva: // 89 - Mandriva
1087 osTypeVBox = SchemaDefs_OSTypeId_Mandriva;
1088 break;
1089
1090 case CIMOSType_CIMOS_Mandriva_64: // 90 - Mandriva 64-Bit
1091 osTypeVBox = SchemaDefs_OSTypeId_Mandriva_64;
1092 break;
1093
1094 case CIMOSType_CIMOS_Ubuntu: // 93 - Ubuntu
1095 osTypeVBox = SchemaDefs_OSTypeId_Ubuntu;
1096 break;
1097
1098 case CIMOSType_CIMOS_Ubuntu_64: // 94 - Ubuntu 64-Bit
1099 osTypeVBox = SchemaDefs_OSTypeId_Ubuntu_64;
1100 break;
1101
1102 case CIMOSType_CIMOS_Debian: // 95 - Debian
1103 osTypeVBox = SchemaDefs_OSTypeId_Debian;
1104 break;
1105
1106 case CIMOSType_CIMOS_Debian_64: // 96 - Debian 64-Bit
1107 osTypeVBox = SchemaDefs_OSTypeId_Debian_64;
1108 break;
1109
1110 case CIMOSType_CIMOS_Linux_2_4_x: // 97 - Linux 2.4.x
1111 osTypeVBox = SchemaDefs_OSTypeId_Linux24;
1112 break;
1113
1114 case CIMOSType_CIMOS_Linux_2_4_x_64: // 98 - Linux 2.4.x 64-Bit
1115 osTypeVBox = SchemaDefs_OSTypeId_Linux24_64;
1116 break;
1117
1118 case CIMOSType_CIMOS_Linux_2_6_x: // 99 - Linux 2.6.x
1119 osTypeVBox = SchemaDefs_OSTypeId_Linux26;
1120 break;
1121
1122 case CIMOSType_CIMOS_Linux_2_6_x_64: // 100 - Linux 2.6.x 64-Bit
1123 osTypeVBox = SchemaDefs_OSTypeId_Linux26_64;
1124 break;
1125 default:
1126 {
1127 /* If we are here we have no clue what OS this should be. Set
1128 to other type as default. */
1129 osTypeVBox = SchemaDefs_OSTypeId_Other;
1130 }
1131 }
1132}
1133
1134STDMETHODIMP Appliance::Read(IN_BSTR path)
1135{
1136 HRESULT rc = S_OK;
1137
1138 if (!path)
1139 return E_POINTER;
1140
1141 AutoCaller autoCaller(this);
1142 CheckComRCReturnRC(autoCaller.rc());
1143
1144 AutoWriteLock alock(this);
1145 m->bstrPath = path;
1146
1147 // see if we can handle this file; for now we insist it has an ".ovf" extension
1148 Utf8Str utf8Path(path);
1149 const char *pcszLastDot = strrchr(utf8Path, '.');
1150 if ( (!pcszLastDot)
1151 || ( strcmp(pcszLastDot, ".ovf")
1152 && strcmp(pcszLastDot, ".OVF")
1153 )
1154 )
1155 return setError(VBOX_E_FILE_ERROR,
1156 tr("Appliance file must have .ovf extension"));
1157
1158 try
1159 {
1160 xml::XmlFileParser parser;
1161 xml::Document doc;
1162 parser.read(utf8Path.raw(),
1163 doc);
1164
1165 const xml::Node *pRootElem = doc.getRootElement();
1166 if (strcmp(pRootElem->getName(), "Envelope"))
1167 return setError(VBOX_E_FILE_ERROR,
1168 tr("Root element in OVF file must be \"Envelope\"."));
1169
1170 // OVF has the following rough layout:
1171 /*
1172 -- <References> .... files referenced from other parts of the file, such as VMDK images
1173 -- Metadata, comprised of several section commands
1174 -- virtual machines, either a single <VirtualSystem>, or a <VirtualSystemCollection>
1175 -- optionally <Strings> for localization
1176 */
1177
1178 // get all "File" child elements of "References" section so we can look up files easily;
1179 // first find the "References" sections so we can look up files
1180 xml::NodesList listFileElements; // receives all /Envelope/References/File nodes
1181 const xml::Node *pReferencesElem;
1182 if ((pReferencesElem = pRootElem->findChildElement("References")))
1183 pReferencesElem->getChildElements(listFileElements, "File");
1184
1185 // now go though the sections
1186 if (!(SUCCEEDED(rc = LoopThruSections(utf8Path.raw(), pReferencesElem, pRootElem))))
1187 return rc;
1188 }
1189 catch(xml::Error &x)
1190 {
1191 return setError(VBOX_E_FILE_ERROR,
1192 x.what());
1193 }
1194
1195 return S_OK;
1196}
1197
1198STDMETHODIMP Appliance::Interpret()
1199{
1200 // @todo:
1201 // - don't use COM methods but the methods directly (faster, but needs appropriate locking of that objects itself (s. HardDisk))
1202 // - Appropriate handle errors like not supported file formats
1203 AutoCaller autoCaller(this);
1204 CheckComRCReturnRC(autoCaller.rc());
1205
1206 AutoWriteLock(this);
1207
1208 HRESULT rc = S_OK;
1209
1210 /* Clear any previous virtual system descriptions */
1211 // @todo: have the entries deleted also?
1212 m->virtualSystemDescriptions.clear();
1213
1214 /* We need the default path for storing disk images */
1215 ComPtr<ISystemProperties> systemProps;
1216 rc = mVirtualBox->COMGETTER(SystemProperties)(systemProps.asOutParam());
1217 CheckComRCReturnRC(rc);
1218 Bstr bstrDefaultHardDiskLocation;
1219 rc = systemProps->COMGETTER(DefaultHardDiskFolder)(bstrDefaultHardDiskLocation.asOutParam());
1220 CheckComRCReturnRC(rc);
1221
1222 /* Try/catch so we can clean up on error */
1223 try
1224 {
1225 list<VirtualSystem>::const_iterator it;
1226 /* Iterate through all virtual systems */
1227 for (it = m->llVirtualSystems.begin();
1228 it != m->llVirtualSystems.end();
1229 ++it)
1230 {
1231 const VirtualSystem &vsysThis = *it;
1232
1233 ComObjPtr<VirtualSystemDescription> pNewDesc;
1234 rc = pNewDesc.createObject();
1235 CheckComRCThrowRC(rc);
1236 rc = pNewDesc->init();
1237 CheckComRCThrowRC(rc);
1238
1239 /* Guest OS type */
1240 Utf8Str strOsTypeVBox,
1241 strCIMOSType = Utf8StrFmt("%RI32", (uint32_t)vsysThis.cimos);
1242 convertCIMOSType2VBoxOSType(strOsTypeVBox, vsysThis.cimos);
1243 pNewDesc->addEntry(VirtualSystemDescriptionType_OS,
1244 "",
1245 strCIMOSType,
1246 strOsTypeVBox);
1247
1248 /* VM name */
1249 /* If the there isn't any name specified create a default one out of
1250 * the OS type */
1251 Utf8Str nameVBox = vsysThis.strName;
1252 if (nameVBox == "")
1253 nameVBox = strOsTypeVBox;
1254 searchUniqueVMName(nameVBox);
1255 pNewDesc->addEntry(VirtualSystemDescriptionType_Name,
1256 "",
1257 vsysThis.strName,
1258 nameVBox);
1259
1260 /* Now that we know the OS type, get our internal defaults based on that. */
1261 ComPtr<IGuestOSType> pGuestOSType;
1262 rc = mVirtualBox->GetGuestOSType(Bstr(strOsTypeVBox), pGuestOSType.asOutParam());
1263 CheckComRCThrowRC(rc);
1264
1265 /* CPU count */
1266 ULONG cpuCountVBox = vsysThis.cCPUs;
1267 /* Check for the constrains */
1268 if (cpuCountVBox > 1) //SchemaDefs::MaxCPUCount)
1269 {
1270 pNewDesc->addWarning(tr("The virtual system claims support for %u CPU's, but VirtualBox has support for max %u CPU's only."),
1271 cpuCountVBox, 1); //SchemaDefs::MaxCPUCount);
1272 cpuCountVBox = 1; //SchemaDefs::MaxCPUCount;
1273 }
1274 if (vsysThis.cCPUs == 0)
1275 cpuCountVBox = 1;
1276 pNewDesc->addEntry(VirtualSystemDescriptionType_CPU,
1277 "",
1278 Utf8StrFmt("%RI32", (uint32_t)vsysThis.cCPUs),
1279 Utf8StrFmt("%RI32", (uint32_t)cpuCountVBox));
1280
1281 /* RAM */
1282 uint64_t ullMemSizeVBox = vsysThis.ullMemorySize / _1M;
1283 /* Check for the constrains */
1284 if (ullMemSizeVBox != 0 &&
1285 (ullMemSizeVBox < static_cast<uint64_t>(SchemaDefs::MinGuestRAM) ||
1286 ullMemSizeVBox > static_cast<uint64_t>(SchemaDefs::MaxGuestRAM)))
1287 {
1288 pNewDesc->addWarning(tr("The virtual system claims support for %llu MB RAM size, but VirtualBox has support for min %u & max %u MB RAM size only."),
1289 ullMemSizeVBox, SchemaDefs::MinGuestRAM, SchemaDefs::MaxGuestRAM);
1290 ullMemSizeVBox = RT_MIN(RT_MAX(ullMemSizeVBox, static_cast<uint64_t>(SchemaDefs::MinGuestRAM)), static_cast<uint64_t>(SchemaDefs::MaxGuestRAM));
1291 }
1292 if (vsysThis.ullMemorySize == 0)
1293 {
1294 /* If the RAM of the OVF is zero, use our predefined values */
1295 ULONG memSizeVBox2;
1296 rc = pGuestOSType->COMGETTER(RecommendedRAM)(&memSizeVBox2);
1297 CheckComRCThrowRC(rc);
1298 /* VBox stores that in MByte */
1299 ullMemSizeVBox = (uint64_t)memSizeVBox2;
1300 }
1301 pNewDesc->addEntry(VirtualSystemDescriptionType_Memory,
1302 "",
1303 Utf8StrFmt("%RI64", (uint64_t)vsysThis.ullMemorySize),
1304 Utf8StrFmt("%RI64", (uint64_t)ullMemSizeVBox));
1305
1306 /* Audio */
1307 if (!vsysThis.strSoundCardType.isNull())
1308 /* Currently we set the AC97 always.
1309 @todo: figure out the hardware which could be possible */
1310 pNewDesc->addEntry(VirtualSystemDescriptionType_SoundCard,
1311 "",
1312 vsysThis.strSoundCardType,
1313 Utf8StrFmt("%RI32", (uint32_t)AudioControllerType_AC97));
1314
1315 /* USB Controller */
1316 if (vsysThis.fHasUsbController)
1317 pNewDesc->addEntry(VirtualSystemDescriptionType_USBController, "", "", "");
1318
1319 NetworksMap::const_iterator itN;
1320 for (itN = m->mapNetworks.begin();
1321 itN != m->mapNetworks.end();
1322 ++itN)
1323 {
1324 const Network &nw = itN->second;
1325 pNewDesc->addEntry(VirtualSystemDescriptionType_LogicalNetwork,
1326 "",
1327 nw.strNetworkName,
1328 nw.strNetworkName);
1329 }
1330
1331 /* Network Controller */
1332 // @todo: there is no hardware specification in the OVF file; supposedly the
1333 // hardware will then be determined by the VirtualSystemType element (e.g. "vmx-07")
1334 if (vsysThis.llNetworkNames.size() > 0)
1335 {
1336 /* Check for the constrains */
1337 if (vsysThis.llNetworkNames.size() > SchemaDefs::NetworkAdapterCount)
1338 {
1339 pNewDesc->addWarning(tr("The virtual system claims support for %u network adapters, but VirtualBox has support for max %u network adapter only."),
1340 vsysThis.llNetworkNames.size(), SchemaDefs::NetworkAdapterCount);
1341
1342 }
1343 /* Get the default network adapter type for the selected guest OS */
1344 NetworkAdapterType_T nwAdapterVBox = NetworkAdapterType_Am79C970A;
1345 rc = pGuestOSType->COMGETTER(AdapterType)(&nwAdapterVBox);
1346 CheckComRCThrowRC(rc);
1347 list<Utf8Str>::const_iterator nwIt;
1348 /* Iterate through all abstract networks. We support 8 network
1349 * adapters at the maximum, so the first 8 will be added only. */
1350 size_t a = 0;
1351 for (nwIt = vsysThis.llNetworkNames.begin();
1352 nwIt != vsysThis.llNetworkNames.end() && a < SchemaDefs::NetworkAdapterCount;
1353 ++nwIt, ++a)
1354 {
1355 Utf8Str strNetwork = *nwIt; // logical network to connect to
1356 pNewDesc->addEntry(VirtualSystemDescriptionType_NetworkAdapter,
1357 "", // ref
1358 strNetwork, // orig
1359 Utf8StrFmt("%RI32", (uint32_t)nwAdapterVBox), // conf
1360 Utf8StrFmt("network=%s", strNetwork.c_str())); // extra conf
1361 }
1362 }
1363
1364 /* Floppy Drive */
1365 if (vsysThis.fHasFloppyDrive)
1366 pNewDesc->addEntry(VirtualSystemDescriptionType_Floppy, "", "", "");
1367
1368 /* CD Drive */
1369 /* @todo: I can't disable the CDROM. So nothing to do for now */
1370 /*
1371 if (vsysThis.fHasCdromDrive)
1372 pNewDesc->addEntry(VirtualSystemDescriptionType_CDROM, "", "", "");*/
1373
1374 /* Hard disk Controller */
1375 uint16_t cIDEused = 0;
1376 uint16_t cSATAused = 0;
1377 uint16_t cSCSIused = 0;
1378 ControllersMap::const_iterator hdcIt;
1379 /* Iterate through all hard disk controllers */
1380 for (hdcIt = vsysThis.mapControllers.begin();
1381 hdcIt != vsysThis.mapControllers.end();
1382 ++hdcIt)
1383 {
1384 const HardDiskController &hdc = hdcIt->second;
1385 Utf8Str strControllerID = Utf8StrFmt("%RI32", (uint32_t)hdc.idController);
1386
1387 switch (hdc.system)
1388 {
1389 case HardDiskController::IDE:
1390 {
1391 /* Check for the constrains */
1392 /* @todo: I'm very confused! Are these bits *one* controller or
1393 is every port/bus declared as an extra controller. */
1394 if (cIDEused < 4)
1395 {
1396 // @todo: figure out the IDE types
1397 /* Use PIIX4 as default */
1398 Utf8Str strType = "PIIX4";
1399 if (!RTStrICmp(hdc.strControllerType.c_str(), "PIIX3"))
1400 strType = "PIIX3";
1401 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerIDE,
1402 strControllerID,
1403 hdc.strControllerType,
1404 strType);
1405 }
1406 else
1407 {
1408 /* Warn only once */
1409 if (cIDEused == 1)
1410 pNewDesc->addWarning(tr("The virtual system claims support for more than one IDE controller, but VirtualBox has support for only one."));
1411
1412 }
1413 ++cIDEused;
1414 break;
1415 }
1416
1417#ifdef VBOX_WITH_AHCI
1418 case HardDiskController::SATA:
1419 {
1420 /* Check for the constrains */
1421 if (cSATAused < 1)
1422 {
1423 // @todo: figure out the SATA types
1424 /* We only support a plain AHCI controller, so use them always */
1425 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSATA,
1426 strControllerID,
1427 hdc.strControllerType,
1428 "AHCI");
1429 }
1430 else
1431 {
1432 /* Warn only once */
1433 if (cSATAused == 1)
1434 pNewDesc->addWarning(tr("The virtual system claims support for more than one SATA controller, but VirtualBox has support for only one."));
1435
1436 }
1437 ++cSATAused;
1438 break;
1439 }
1440#endif /* VBOX_WITH_AHCI */
1441
1442 case HardDiskController::SCSI:
1443 {
1444 /* Check for the constrains */
1445 if (cSCSIused < 1)
1446 {
1447 // @todo: figure out the SCSI types
1448 Utf8Str hdcController = "LsiLogic";
1449 /* if (!RTStrICmp(hdc.strControllerType.c_str(), "LsiLogic"))
1450 hdcController = "LsiLogic";
1451 else*/
1452 if (!RTStrICmp(hdc.strControllerType.c_str(), "BusLogic"))
1453 hdcController = "BusLogic";
1454 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSCSI,
1455 strControllerID,
1456 hdc.strControllerType,
1457 hdcController);
1458 }
1459 else
1460 {
1461 /* Warn only once */
1462 if (cSCSIused == 1)
1463 pNewDesc->addWarning(tr("The virtual system claims support for more than one SCSI controller, but VirtualBox has support for only one."));
1464
1465 }
1466 ++cSCSIused;
1467 break;
1468 }
1469 default:
1470 {
1471 /* @todo: should we stop? */
1472 }
1473 }
1474 }
1475
1476 /* Hard disks */
1477 if (vsysThis.mapVirtualDisks.size() > 0)
1478 {
1479 VirtualDisksMap::const_iterator itVD;
1480 /* Iterate through all hard disks ()*/
1481 for (itVD = vsysThis.mapVirtualDisks.begin();
1482 itVD != vsysThis.mapVirtualDisks.end();
1483 ++itVD)
1484 {
1485 const VirtualDisk &hd = itVD->second;
1486 /* Get the associated disk image */
1487 const DiskImage &di = m->mapDisks[hd.strDiskId];
1488
1489 // @todo:
1490 // - figure out all possible vmdk formats we also support
1491 // - figure out if there is a url specifier for vhd already
1492 // - we need a url specifier for the vdi format
1493 if ( (!RTStrICmp(di.strFormat.c_str(), "http://www.vmware.com/specifications/vmdk.html#sparse"))
1494 || (!RTStrICmp(di.strFormat.c_str(), "http://www.vmware.com/specifications/vmdk.html#compressed"))
1495 )
1496 {
1497 /* If the href is empty use the VM name as filename */
1498 Utf8Str strFilename = di.strHref.c_str();
1499 if (di.strHref.c_str()[0] == 0)
1500 strFilename = Utf8StrFmt("%s.vmdk", nameVBox.c_str());
1501 /* Construct a unique target path */
1502 Utf8StrFmt strPath("%ls%c%s",
1503 bstrDefaultHardDiskLocation.raw(),
1504 RTPATH_DELIMITER,
1505 strFilename.c_str());
1506 searchUniqueDiskImageFilePath(strPath);
1507
1508 /* find the description for the hard disk controller
1509 * that has the same ID as hd.idController */
1510 const VirtualSystemDescriptionEntry *pController;
1511 if (!(pController = pNewDesc->findControllerFromID(hd.idController)))
1512 throw setError(E_FAIL,
1513 tr("Internal inconsistency looking up hard disk controller."));
1514
1515 /* controller to attach to @todo bus? */
1516 Utf8StrFmt strExtraConfig("controller=%RI16",
1517 pController->ulIndex);
1518 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskImage,
1519 hd.strDiskId,
1520 di.strHref,
1521 strPath,
1522 strExtraConfig);
1523 }
1524 else
1525 {
1526 /* @todo: should we stop here? */
1527 pNewDesc->addWarning(tr("The virtual system claims support for the following virtual disk image format which VirtualBox not support: %s"),
1528 di.strFormat.c_str());
1529 }
1530 }
1531 }
1532
1533 m->virtualSystemDescriptions.push_back(pNewDesc);
1534 }
1535 }
1536 catch (HRESULT aRC)
1537 {
1538 /* On error we clear the list & return */
1539 m->virtualSystemDescriptions.clear();
1540 rc = aRC;
1541 }
1542
1543 return rc;
1544}
1545
1546STDMETHODIMP Appliance::ImportMachines(IProgress **aProgress)
1547{
1548 CheckComArgOutPointerValid(aProgress);
1549
1550 AutoCaller autoCaller(this);
1551 CheckComRCReturnRC(autoCaller.rc());
1552
1553 AutoReadLock(this);
1554
1555 HRESULT rc = S_OK;
1556
1557 ComObjPtr<Progress> progress;
1558 try
1559 {
1560 /* Figure out how many sub operation the import will need */
1561 /* One for the appliance */
1562 int opCount = 1;
1563 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;
1564 for (it = m->virtualSystemDescriptions.begin();
1565 it != m->virtualSystemDescriptions.end();
1566 ++it)
1567 {
1568 /* One for every Virtual System */
1569 ++opCount;
1570 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);
1571 /* One for every hard disk of the Virtual System */
1572 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
1573 opCount += avsdeHDs.size();
1574 }
1575 Bstr progressDesc = BstrFmt(tr("Import appliance '%ls'"),
1576 m->bstrPath.raw());
1577 /* Create the progress object */
1578 progress.createObject();
1579 rc = progress->init(mVirtualBox, static_cast<IAppliance *>(this),
1580 progressDesc,
1581 FALSE /* aCancelable */,
1582 opCount,
1583 progressDesc);
1584 CheckComRCThrowRC(rc);
1585
1586 /* Initialize our worker task */
1587 std::auto_ptr<Task> task(new Task(this, progress));
1588 //AssertComRCThrowRC (task->autoCaller.rc());
1589
1590 rc = task->startThread();
1591 CheckComRCThrowRC(rc);
1592
1593 task.release();
1594 }
1595 catch (HRESULT aRC)
1596 {
1597 rc = aRC;
1598 }
1599
1600 if (SUCCEEDED(rc))
1601 /* Return progress to the caller */
1602 progress.queryInterfaceTo(aProgress);
1603
1604 return rc;
1605}
1606
1607HRESULT Appliance::searchUniqueVMName(Utf8Str& aName) const
1608{
1609 IMachine *machine = NULL;
1610 char *tmpName = RTStrDup(aName.c_str());
1611 int i = 1;
1612 /* @todo: Maybe too cost-intensive; try to find a lighter way */
1613 while (mVirtualBox->FindMachine(Bstr(tmpName), &machine) != VBOX_E_OBJECT_NOT_FOUND)
1614 {
1615 RTStrFree(tmpName);
1616 RTStrAPrintf(&tmpName, "%s_%d", aName.c_str(), i);
1617 ++i;
1618 }
1619 aName = tmpName;
1620 RTStrFree(tmpName);
1621
1622 return S_OK;
1623}
1624
1625HRESULT Appliance::searchUniqueDiskImageFilePath(Utf8Str& aName) const
1626{
1627 IHardDisk *harddisk = NULL;
1628 char *tmpName = RTStrDup(aName.c_str());
1629 int i = 1;
1630 /* Check if the file exists or if a file with this path is registered
1631 * already */
1632 /* @todo: Maybe too cost-intensive; try to find a lighter way */
1633 while (RTPathExists(tmpName) ||
1634 mVirtualBox->FindHardDisk(Bstr(tmpName), &harddisk) != VBOX_E_OBJECT_NOT_FOUND)
1635 {
1636 RTStrFree(tmpName);
1637 char *tmpDir = RTStrDup(aName.c_str());
1638 RTPathStripFilename(tmpDir);;
1639 char *tmpFile = RTStrDup(RTPathFilename(aName.c_str()));
1640 RTPathStripExt(tmpFile);
1641 const char *tmpExt = RTPathExt(aName.c_str());
1642 RTStrAPrintf(&tmpName, "%s%c%s_%d%s", tmpDir, RTPATH_DELIMITER, tmpFile, i, tmpExt);
1643 RTStrFree(tmpFile);
1644 RTStrFree(tmpDir);
1645 ++i;
1646 }
1647 aName = tmpName;
1648 RTStrFree(tmpName);
1649
1650 return S_OK;
1651}
1652
1653/* static */
1654DECLCALLBACK(int) Appliance::taskThread(RTTHREAD aThread, void *pvUser)
1655{
1656 std::auto_ptr<Task> task(static_cast<Task *>(pvUser));
1657 AssertReturn(task.get(), VERR_GENERAL_FAILURE);
1658
1659 Appliance *app = task->that;
1660
1661 /// @todo ugly hack, fix ComAssert... (same as in HardDisk::taskThread)
1662 #define setError app->setError
1663
1664 LogFlowFuncEnter();
1665 LogFlowFunc(("Appliance %p\n", app));
1666
1667 AutoCaller autoCaller(app);
1668 CheckComRCReturnRC(autoCaller.rc());
1669
1670 AutoWriteLock appLock(app);
1671
1672 HRESULT rc = S_OK;
1673
1674 list<VirtualSystem>::const_iterator it;
1675 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it1;
1676 /* Iterate through all virtual systems of that appliance */
1677 size_t i = 0;
1678 for (it = app->m->llVirtualSystems.begin(),
1679 it1 = app->m->virtualSystemDescriptions.begin();
1680 it != app->m->llVirtualSystems.end();
1681 ++it, ++it1, ++i)
1682 {
1683 const VirtualSystem &vsysThis = *it;
1684 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it1);
1685
1686 /* Catch possible errors */
1687 try
1688 {
1689 if (!task->progress.isNull())
1690 {
1691 rc = task->progress->advanceOperation(BstrFmt(tr("Importing Virtual System %d"), i + 1));
1692 CheckComRCThrowRC(rc);
1693 }
1694
1695 /* How many sub notifications are necessary? */
1696 const float opCountMax = 100.0/5;
1697 uint32_t opCount = 0;
1698
1699 /* Guest OS type */
1700 std::list<VirtualSystemDescriptionEntry*> vsdeOS;
1701 vsdeOS = vsdescThis->findByType(VirtualSystemDescriptionType_OS);
1702 if (vsdeOS.size() < 1)
1703 throw setError(VBOX_E_FILE_ERROR,
1704 tr("Missing guest OS type"));
1705 const Utf8Str &strOsTypeVBox = vsdeOS.front()->strConfig;
1706
1707 /* Now that we know the base system get our internal defaults based on that. */
1708 ComPtr<IGuestOSType> osType;
1709 rc = app->mVirtualBox->GetGuestOSType(Bstr(strOsTypeVBox), osType.asOutParam());
1710 CheckComRCThrowRC(rc);
1711
1712 /* Create the machine */
1713 /* First get the name */
1714 std::list<VirtualSystemDescriptionEntry*> vsdeName = vsdescThis->findByType(VirtualSystemDescriptionType_Name);
1715 if (vsdeName.size() < 1)
1716 throw setError(VBOX_E_FILE_ERROR,
1717 tr("Missing VM name"));
1718 const Utf8Str &strNameVBox = vsdeName.front()->strConfig;
1719 ComPtr<IMachine> pNewMachine;
1720 rc = app->mVirtualBox->CreateMachine(Bstr(strNameVBox), Bstr(strOsTypeVBox),
1721 Bstr(), Guid(),
1722 pNewMachine.asOutParam());
1723 CheckComRCThrowRC(rc);
1724
1725 if (!task->progress.isNull())
1726 {
1727 rc = task->progress->notifyProgress((uint32_t)(opCountMax * opCount++));
1728 CheckComRCThrowRC(rc);
1729 }
1730
1731 /* CPU count (ignored for now) */
1732 // EntriesList vsdeCPU = vsd->findByType (VirtualSystemDescriptionType_CPU);
1733
1734 /* RAM */
1735 std::list<VirtualSystemDescriptionEntry*> vsdeRAM = vsdescThis->findByType(VirtualSystemDescriptionType_Memory);
1736 ComAssertMsgThrow(vsdeRAM.size() == 1, ("RAM size missing"), E_FAIL);
1737 const Utf8Str &memoryVBox = vsdeRAM.front()->strConfig;
1738 ULONG tt = (ULONG)RTStrToUInt64(memoryVBox.c_str());
1739 rc = pNewMachine->COMSETTER(MemorySize)(tt);
1740 CheckComRCThrowRC(rc);
1741
1742 /* VRAM */
1743 /* Get the recommended VRAM for this guest OS type */
1744 ULONG vramVBox;
1745 rc = osType->COMGETTER(RecommendedVRAM)(&vramVBox);
1746 CheckComRCThrowRC(rc);
1747 /* Set the VRAM */
1748 rc = pNewMachine->COMSETTER(VRAMSize)(vramVBox);
1749 CheckComRCThrowRC(rc);
1750
1751 if (!task->progress.isNull())
1752 {
1753 rc = task->progress->notifyProgress((uint32_t)(opCountMax * opCount++));
1754 CheckComRCThrowRC(rc);
1755 }
1756
1757 /* Audio Adapter */
1758 std::list<VirtualSystemDescriptionEntry*> vsdeAudioAdapter = vsdescThis->findByType(VirtualSystemDescriptionType_SoundCard);
1759 /* @todo: we support one audio adapter only */
1760 if (vsdeAudioAdapter.size() > 0)
1761 {
1762 const Utf8Str& audioAdapterVBox = vsdeAudioAdapter.front()->strConfig;
1763 if (RTStrICmp(audioAdapterVBox, "null") != 0)
1764 {
1765 uint32_t audio = RTStrToUInt32(audioAdapterVBox.c_str());
1766 ComPtr<IAudioAdapter> audioAdapter;
1767 rc = pNewMachine->COMGETTER(AudioAdapter)(audioAdapter.asOutParam());
1768 CheckComRCThrowRC(rc);
1769 rc = audioAdapter->COMSETTER(Enabled)(true);
1770 CheckComRCThrowRC(rc);
1771 rc = audioAdapter->COMSETTER(AudioController)(static_cast<AudioControllerType_T>(audio));
1772 CheckComRCThrowRC(rc);
1773 }
1774 }
1775
1776 /* USB Controller */
1777 std::list<VirtualSystemDescriptionEntry*> vsdeUSBController = vsdescThis->findByType(VirtualSystemDescriptionType_USBController);
1778 // USB support is enabled if there's at least one such entry; to disable USB support,
1779 // the type of the USB item would have been changed to "ignore"
1780 bool fUSBEnabled = vsdeUSBController.size() > 0;
1781
1782 ComPtr<IUSBController> usbController;
1783 rc = pNewMachine->COMGETTER(USBController)(usbController.asOutParam());
1784 CheckComRCThrowRC(rc);
1785 rc = usbController->COMSETTER(Enabled)(fUSBEnabled);
1786 CheckComRCThrowRC(rc);
1787
1788 if (!task->progress.isNull())
1789 {
1790 rc = task->progress->notifyProgress((uint32_t)(opCountMax * opCount++));
1791 CheckComRCThrowRC(rc);
1792 }
1793
1794 /* Change the network adapters */
1795 std::list<VirtualSystemDescriptionEntry*> vsdeNW = vsdescThis->findByType(VirtualSystemDescriptionType_NetworkAdapter);
1796 if (vsdeNW.size() == 0)
1797 {
1798 /* No network adapters, so we have to disable our default one */
1799 ComPtr<INetworkAdapter> nwVBox;
1800 rc = pNewMachine->GetNetworkAdapter(0, nwVBox.asOutParam());
1801 CheckComRCThrowRC(rc);
1802 rc = nwVBox->COMSETTER(Enabled)(false);
1803 CheckComRCThrowRC(rc);
1804 }
1805 else
1806 {
1807 list<VirtualSystemDescriptionEntry*>::const_iterator nwIt;
1808 /* Iterate through all network cards. We support 8 network adapters
1809 * at the maximum. (@todo: warn if there are more!) */
1810 size_t a = 0;
1811 for (nwIt = vsdeNW.begin();
1812 (nwIt != vsdeNW.end() && a < SchemaDefs::NetworkAdapterCount);
1813 ++nwIt, ++a)
1814 {
1815 const Utf8Str &nwTypeVBox = (*nwIt)->strConfig;
1816 uint32_t tt1 = RTStrToUInt32(nwTypeVBox.c_str());
1817 ComPtr<INetworkAdapter> nwVBox;
1818 rc = pNewMachine->GetNetworkAdapter((ULONG)a, nwVBox.asOutParam());
1819 CheckComRCThrowRC(rc);
1820 /* Enable the network card & set the adapter type */
1821 /* NAT is set as default */
1822 rc = nwVBox->COMSETTER(Enabled)(true);
1823 CheckComRCThrowRC(rc);
1824 rc = nwVBox->COMSETTER(AdapterType)(static_cast<NetworkAdapterType_T>(tt1));
1825 CheckComRCThrowRC(rc);
1826 }
1827 }
1828
1829 /* Floppy drive */
1830 std::list<VirtualSystemDescriptionEntry*> vsdeFloppy = vsdescThis->findByType(VirtualSystemDescriptionType_Floppy);
1831 // Floppy support is enabled if there's at least one such entry; to disable floppy support,
1832 // the type of the floppy item would have been changed to "ignore"
1833 bool fFloppyEnabled = vsdeFloppy.size() > 0;
1834 ComPtr<IFloppyDrive> floppyDrive;
1835 rc = pNewMachine->COMGETTER(FloppyDrive)(floppyDrive.asOutParam());
1836 CheckComRCThrowRC(rc);
1837 rc = floppyDrive->COMSETTER(Enabled)(fFloppyEnabled);
1838 CheckComRCThrowRC(rc);
1839
1840 if (!task->progress.isNull())
1841 {
1842 rc = task->progress->notifyProgress((uint32_t)(opCountMax * opCount++));
1843 CheckComRCThrowRC(rc);
1844 }
1845
1846 /* CDROM drive */
1847 /* @todo: I can't disable the CDROM. So nothing to do for now */
1848 // std::list<VirtualSystemDescriptionEntry*> vsdeFloppy = vsd->findByType(VirtualSystemDescriptionType_CDROM);
1849
1850 /* Hard disk controller IDE */
1851 std::list<VirtualSystemDescriptionEntry*> vsdeHDCIDE = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerIDE);
1852 /* @todo: we support one IDE controller only */
1853 if (vsdeHDCIDE.size() > 0)
1854 {
1855 /* Set the appropriate IDE controller in the virtual BIOS of the VM */
1856 ComPtr<IBIOSSettings> biosSettings;
1857 rc = pNewMachine->COMGETTER(BIOSSettings)(biosSettings.asOutParam());
1858 CheckComRCThrowRC(rc);
1859 const char *pcszIDEType = vsdeHDCIDE.front()->strConfig.c_str();
1860 if (!strcmp(pcszIDEType, "PIIX3"))
1861 rc = biosSettings->COMSETTER(IDEControllerType)(IDEControllerType_PIIX3);
1862 else if (!strcmp(pcszIDEType, "PIIX4"))
1863 rc = biosSettings->COMSETTER(IDEControllerType)(IDEControllerType_PIIX4);
1864 else
1865 throw setError(VBOX_E_FILE_ERROR,
1866 tr("Invalid IDE controller type \"%s\""),
1867 pcszIDEType);
1868 CheckComRCThrowRC(rc);
1869 }
1870#ifdef VBOX_WITH_AHCI
1871 /* Hard disk controller SATA */
1872 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSATA = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSATA);
1873 /* @todo: we support one SATA controller only */
1874 if (vsdeHDCSATA.size() > 0)
1875 {
1876 const Utf8Str &hdcVBox = vsdeHDCIDE.front()->strConfig;
1877 if (hdcVBox == "AHCI")
1878 {
1879 /* For now we have just to enable the AHCI controller. */
1880 ComPtr<ISATAController> hdcSATAVBox;
1881 rc = pNewMachine->COMGETTER(SATAController)(hdcSATAVBox.asOutParam());
1882 CheckComRCThrowRC(rc);
1883 rc = hdcSATAVBox->COMSETTER(Enabled)(true);
1884 CheckComRCThrowRC(rc);
1885 }
1886 else
1887 {
1888 throw setError(VBOX_E_FILE_ERROR,
1889 tr("Invalid SATA controller type \"%s\""),
1890 hdcVBox.c_str());
1891 }
1892 }
1893#endif /* VBOX_WITH_AHCI */
1894
1895 /* Hard disk controller SCSI */
1896 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSCSI = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSCSI);
1897 /* @todo: do we support more than one SCSI controller? */
1898 if (vsdeHDCSCSI.size() > 0)
1899 {
1900 /* @todo: revisit when Main support for SCSI is ready */
1901 }
1902
1903 /* Now its time to register the machine before we add any hard disks */
1904 rc = app->mVirtualBox->RegisterMachine(pNewMachine);
1905 CheckComRCThrowRC(rc);
1906
1907 if (!task->progress.isNull())
1908 {
1909 rc = task->progress->notifyProgress((uint32_t)(opCountMax * opCount++));
1910 CheckComRCThrowRC(rc);
1911 }
1912
1913 /* Create the hard disks & connect them to the appropriate controllers. */
1914 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
1915 if (avsdeHDs.size() > 0)
1916 {
1917 Guid newMachineId;
1918 rc = pNewMachine->COMGETTER(Id)(newMachineId.asOutParam());
1919 CheckComRCThrowRC(rc);
1920 /* If in the next block an error occur we have to deregister
1921 the machine, so make an extra try/catch block. */
1922 ComPtr<ISession> session;
1923 bool fSessionOpen = false;
1924 ComPtr<IHardDisk> srcHdVBox;
1925
1926 try
1927 {
1928 /* In order to attach hard disks we need to open a session
1929 * for the new machine */
1930 rc = session.createInprocObject(CLSID_Session);
1931 CheckComRCThrowRC(rc);
1932 rc = app->mVirtualBox->OpenSession(session, newMachineId);
1933 CheckComRCThrowRC(rc);
1934 fSessionOpen = true;
1935
1936 int result;
1937 /* The disk image has to be on the same place as the OVF file. So
1938 * strip the filename out of the full file path. */
1939 Utf8Str strSrcDir = stripFilename(Utf8Str(app->m->bstrPath).raw());
1940
1941 /* Iterate over all given disk images */
1942 list<VirtualSystemDescriptionEntry*>::const_iterator itHD;
1943 for (itHD = avsdeHDs.begin();
1944 itHD != avsdeHDs.end();
1945 ++itHD)
1946 {
1947 VirtualSystemDescriptionEntry *vsdeHD = *itHD;
1948
1949 const char *pcszDstFilePath = vsdeHD->strConfig.c_str();
1950 /* Check if the destination file exists already or the
1951 * destination path is empty. */
1952 if ( !(*pcszDstFilePath)
1953 || RTPathExists(pcszDstFilePath)
1954 )
1955 /* This isn't allowed */
1956 throw setError(VBOX_E_FILE_ERROR,
1957 tr("Destination file '%s' exists",
1958 pcszDstFilePath));
1959
1960 /* Find the disk from the OVF's disk list */
1961 DiskImagesMap::const_iterator itDiskImage = app->m->mapDisks.find(vsdeHD->strRef);
1962 /* vsdeHD->strRef contains the disk identifier (e.g. "vmdisk1"), which should exist
1963 in the virtual system's disks map under that ID and also in the global images map. */
1964 VirtualDisksMap::const_iterator itVirtualDisk = vsysThis.mapVirtualDisks.find(vsdeHD->strRef);
1965
1966 if ( itDiskImage == app->m->mapDisks.end()
1967 || itVirtualDisk == vsysThis.mapVirtualDisks.end()
1968 )
1969 throw setError(E_FAIL,
1970 tr("Internal inconsistency looking up disk images."));
1971
1972 const DiskImage &di = itDiskImage->second;
1973 const VirtualDisk &vd = itVirtualDisk->second;
1974
1975 /* Make sure all target directories exists */
1976 rc = VirtualBox::ensureFilePathExists(pcszDstFilePath);
1977 CheckComRCThrowRC(rc);
1978 ComPtr<IHardDisk> dstHdVBox;
1979 /* If strHref is empty we have to create a new file */
1980 if (di.strHref.c_str()[0] == 0)
1981 {
1982 /* Which format to use? */
1983 Bstr srcFormat = L"VDI";
1984 if ( (!RTStrICmp(di.strFormat.c_str(), "http://www.vmware.com/specifications/vmdk.html#sparse"))
1985 || (!RTStrICmp(di.strFormat.c_str(), "http://www.vmware.com/specifications/vmdk.html#compressed")))
1986 srcFormat = L"VMDK";
1987 /* Create an empty hard disk */
1988 rc = app->mVirtualBox->CreateHardDisk(srcFormat, Bstr(pcszDstFilePath), dstHdVBox.asOutParam());
1989 CheckComRCThrowRC(rc);
1990 /* Create a dynamic growing disk image with the given capacity */
1991 ComPtr<IProgress> progress;
1992 dstHdVBox->CreateDynamicStorage(di.iCapacity / _1M, progress.asOutParam());
1993 CheckComRCThrowRC(rc);
1994 /* Advance to the next operation */
1995 if (!task->progress.isNull())
1996 {
1997 rc = task->progress->advanceOperation (BstrFmt(tr("Creating virtual disk image '%s'"), pcszDstFilePath));
1998 CheckComRCThrowRC(rc);
1999 }
2000 /* Manually check the progress to inform the user */
2001 BOOL fCompleted;
2002 LONG currentPercent;
2003 while (SUCCEEDED(progress->COMGETTER(Completed(&fCompleted))))
2004 {
2005 rc = progress->COMGETTER(Percent(&currentPercent));
2006 CheckComRCThrowRC(rc);
2007 if (!task->progress.isNull())
2008 {
2009 rc = task->progress->notifyProgress(currentPercent);
2010 CheckComRCThrowRC(rc);
2011 }
2012 if (fCompleted)
2013 break;
2014 /* Make sure the loop is not too tight */
2015 rc = progress->WaitForCompletion(100);
2016 CheckComRCThrowRC(rc);
2017 }
2018 /* Check the real return code of the asynchrony operation */
2019 HRESULT vrc;
2020 rc = progress->COMGETTER(ResultCode)(&vrc);
2021 CheckComRCThrowRC(rc);
2022 CheckComRCThrowRC(vrc);
2023 }
2024 else
2025 {
2026 /* Construct the source file path */
2027 Utf8StrFmt strSrcFilePath("%s%c%s", strSrcDir.c_str(), RTPATH_DELIMITER, di.strHref.c_str());
2028 /* Check if the source file exists */
2029 if (!RTPathExists(strSrcFilePath.c_str()))
2030 /* This isn't allowed */
2031 throw setError(VBOX_E_FILE_ERROR,
2032 tr("Source virtual disk image file '%s' doesn't exist"),
2033 strSrcFilePath.c_str());
2034 /* Clone the disk image (this is necessary cause the id has
2035 * to be recreated for the case the same hard disk is
2036 * attached already from a previous import) */
2037 /* First open the existing disk image */
2038 rc = app->mVirtualBox->OpenHardDisk(Bstr(strSrcFilePath), srcHdVBox.asOutParam());
2039 CheckComRCThrowRC(rc);
2040 /* We need the format description of the source disk image */
2041 Bstr srcFormat;
2042 rc = srcHdVBox->COMGETTER(Format)(srcFormat.asOutParam());
2043 CheckComRCThrowRC(rc);
2044 /* Create a new hard disk interface for the destination disk image */
2045 rc = app->mVirtualBox->CreateHardDisk(srcFormat, Bstr(pcszDstFilePath), dstHdVBox.asOutParam());
2046 CheckComRCThrowRC(rc);
2047 /* Clone the source disk image */
2048 ComPtr<IProgress> progress;
2049 rc = srcHdVBox->CloneTo(dstHdVBox, progress.asOutParam());
2050 CheckComRCThrowRC(rc);
2051 /* Advance to the next operation */
2052 if (!task->progress.isNull())
2053 {
2054 rc = task->progress->advanceOperation (BstrFmt(tr("Importing virtual disk image '%s'"), strSrcFilePath.c_str()));
2055 CheckComRCThrowRC(rc);
2056 }
2057 /* Manually check the progress to inform the user */
2058 BOOL fCompleted;
2059 LONG currentPercent;
2060 while (SUCCEEDED(progress->COMGETTER(Completed(&fCompleted))))
2061 {
2062 rc = progress->COMGETTER(Percent(&currentPercent));
2063 CheckComRCThrowRC(rc);
2064 if (!task->progress.isNull())
2065 {
2066 rc = task->progress->notifyProgress (currentPercent);
2067 CheckComRCThrowRC(rc);
2068 }
2069 if (fCompleted)
2070 break;
2071 /* Make sure the loop is not too tight */
2072 rc = progress->WaitForCompletion(100);
2073 CheckComRCThrowRC(rc);
2074 }
2075 /* Check the real return code of the asynchrony operation */
2076 HRESULT vrc;
2077 rc = progress->COMGETTER(ResultCode)(&vrc);
2078 CheckComRCThrowRC(rc);
2079 CheckComRCThrowRC(vrc);
2080 /* We *must* close the source disk image in order to deregister it */
2081 rc = srcHdVBox->Close();
2082 CheckComRCThrowRC(rc);
2083 }
2084 /* Now use the new uuid to attach the disk image to our new machine */
2085 ComPtr<IMachine> sMachine;
2086 rc = session->COMGETTER(Machine)(sMachine.asOutParam());
2087 CheckComRCThrowRC(rc);
2088 Guid hdId;
2089 rc = dstHdVBox->COMGETTER(Id)(hdId.asOutParam());;
2090 CheckComRCThrowRC(rc);
2091 /* For now we assume we have one controller of every type only */
2092 HardDiskController hdc = (*vsysThis.mapControllers.find(vd.idController)).second;
2093 StorageBus_T sbt = StorageBus_IDE;
2094 switch (hdc.system)
2095 {
2096 case HardDiskController::IDE: sbt = StorageBus_IDE; break;
2097 case HardDiskController::SATA: sbt = StorageBus_SATA; break;
2098 //case HardDiskController::SCSI: sbt = StorageBus_SCSI; break; // @todo: not available yet
2099 default: break;
2100 }
2101 rc = sMachine->AttachHardDisk(hdId, sbt, hdc.ulBusNumber, 0);
2102 CheckComRCThrowRC(rc);
2103 rc = sMachine->SaveSettings();
2104 CheckComRCThrowRC(rc);
2105 } // end for (itHD = avsdeHDs.begin();
2106
2107 // only now that we're done with all disks, close the session
2108 rc = session->Close();
2109 CheckComRCThrowRC(rc);
2110 }
2111 catch(HRESULT aRC)
2112 {
2113 /* @todo: Not sure what to do when there are successfully
2114 added disk images. Delete them also? For now we leave
2115 them. */
2116 if (!srcHdVBox.isNull())
2117 {
2118 /* Close any open src disks */
2119 rc = srcHdVBox->Close();
2120 CheckComRCThrowRC(rc);
2121 }
2122 if (!session.isNull())
2123 {
2124 /* If there is an open session, close them before doing
2125 anything further. */
2126 rc = session->Close();
2127 CheckComRCThrowRC(rc);
2128 }
2129 /* Unregister/Delete the failed machine */
2130 ComPtr<IMachine> failedMachine;
2131 rc = app->mVirtualBox->UnregisterMachine(newMachineId, failedMachine.asOutParam());
2132 CheckComRCThrowRC(rc);
2133 rc = failedMachine->DeleteSettings();
2134 CheckComRCThrowRC(rc);
2135 /* Throw the original error */
2136 throw;
2137 }
2138 }
2139 }
2140 catch(HRESULT aRC)
2141 {
2142 /* @todo: If we are here an error on importing of *one* virtual
2143 system has occurred. We didn't break now, but try to import the
2144 other virtual systems. This needs some further discussion. */
2145 rc = aRC;
2146 }
2147 }
2148
2149 task->rc = rc;
2150
2151 if (!task->progress.isNull())
2152 task->progress->notifyComplete (rc);
2153
2154 LogFlowFunc(("rc=%Rhrc\n", rc));
2155 LogFlowFuncLeave();
2156
2157 /// @todo ugly hack, fix ComAssert... (same as in HardDisk::taskThread)
2158 #undef setError
2159
2160 return VINF_SUCCESS;
2161}
2162
2163// IVirtualSystemDescription constructor / destructor
2164////////////////////////////////////////////////////////////////////////////////
2165
2166DEFINE_EMPTY_CTOR_DTOR(VirtualSystemDescription)
2167struct shutup3 {};
2168
2169struct VirtualSystemDescription::Data
2170{
2171 list<VirtualSystemDescriptionEntry> descriptions;
2172 list<Utf8Str> warnings;
2173};
2174
2175HRESULT VirtualSystemDescription::init()
2176{
2177 /* Enclose the state transition NotReady->InInit->Ready */
2178 AutoInitSpan autoInitSpan(this);
2179 AssertReturn(autoInitSpan.isOk(), E_FAIL);
2180
2181 /* Initialize data */
2182 m = new Data();
2183
2184 /* Confirm a successful initialization */
2185 autoInitSpan.setSucceeded();
2186
2187 return S_OK;
2188}
2189
2190void VirtualSystemDescription::uninit()
2191{
2192 delete m;
2193 m = NULL;
2194}
2195
2196STDMETHODIMP VirtualSystemDescription::COMGETTER(Count)(ULONG *aCount)
2197{
2198 if (!aCount)
2199 return E_POINTER;
2200
2201 AutoCaller autoCaller(this);
2202 CheckComRCReturnRC(autoCaller.rc());
2203
2204 AutoReadLock alock(this);
2205
2206 *aCount = (ULONG)m->descriptions.size();
2207
2208 return S_OK;
2209}
2210
2211STDMETHODIMP VirtualSystemDescription::GetDescription(ComSafeArrayOut(VirtualSystemDescriptionType_T, aTypes),
2212 ComSafeArrayOut(BSTR, aRefs),
2213 ComSafeArrayOut(BSTR, aOrigValues),
2214 ComSafeArrayOut(BSTR, aConfigValues),
2215 ComSafeArrayOut(BSTR, aExtraConfigValues))
2216{
2217 if (ComSafeArrayOutIsNull(aTypes) ||
2218 ComSafeArrayOutIsNull(aRefs) ||
2219 ComSafeArrayOutIsNull(aOrigValues) ||
2220 ComSafeArrayOutIsNull(aConfigValues) ||
2221 ComSafeArrayOutIsNull(aExtraConfigValues))
2222 return E_POINTER;
2223
2224 AutoCaller autoCaller(this);
2225 CheckComRCReturnRC(autoCaller.rc());
2226
2227 AutoReadLock alock(this);
2228
2229 ULONG c = (ULONG)m->descriptions.size();
2230 com::SafeArray<VirtualSystemDescriptionType_T> sfaTypes(c);
2231 com::SafeArray<BSTR> sfaRefs(c);
2232 com::SafeArray<BSTR> sfaOrigValues(c);
2233 com::SafeArray<BSTR> sfaConfigValues(c);
2234 com::SafeArray<BSTR> sfaExtraConfigValues(c);
2235
2236 list<VirtualSystemDescriptionEntry>::const_iterator it;
2237 size_t i = 0;
2238 for (it = m->descriptions.begin();
2239 it != m->descriptions.end();
2240 ++it, ++i)
2241 {
2242 const VirtualSystemDescriptionEntry &vsde = (*it);
2243
2244 sfaTypes[i] = vsde.type;
2245
2246 Bstr bstr = vsde.strRef;
2247 bstr.cloneTo(&sfaRefs[i]);
2248
2249 bstr = vsde.strOrig;
2250 bstr.cloneTo(&sfaOrigValues[i]);
2251
2252 bstr = vsde.strConfig;
2253 bstr.cloneTo(&sfaConfigValues[i]);
2254
2255 bstr = vsde.strExtraConfig;
2256 bstr.cloneTo(&sfaExtraConfigValues[i]);
2257 }
2258
2259 sfaTypes.detachTo(ComSafeArrayOutArg(aTypes));
2260 sfaRefs.detachTo(ComSafeArrayOutArg(aRefs));
2261 sfaOrigValues.detachTo(ComSafeArrayOutArg(aOrigValues));
2262 sfaConfigValues.detachTo(ComSafeArrayOutArg(aConfigValues));
2263 sfaExtraConfigValues.detachTo(ComSafeArrayOutArg(aExtraConfigValues));
2264
2265 return S_OK;
2266}
2267
2268STDMETHODIMP VirtualSystemDescription::SetFinalValues(ComSafeArrayIn(BOOL, aEnabled),
2269 ComSafeArrayIn(IN_BSTR, argConfigValues),
2270 ComSafeArrayIn(IN_BSTR, argExtraConfigValues))
2271{
2272 CheckComArgSafeArrayNotNull(argConfigValues);
2273 CheckComArgSafeArrayNotNull(argExtraConfigValues);
2274
2275 AutoCaller autoCaller(this);
2276 CheckComRCReturnRC(autoCaller.rc());
2277
2278 AutoWriteLock alock(this);
2279
2280 com::SafeArray<IN_BSTR> aConfigValues(ComSafeArrayInArg(argConfigValues));
2281 com::SafeArray<IN_BSTR> aExtraConfigValues(ComSafeArrayInArg(argExtraConfigValues));
2282
2283 if ( (aConfigValues.size() != m->descriptions.size())
2284 || (aExtraConfigValues.size() != m->descriptions.size())
2285 )
2286 return E_INVALIDARG;
2287
2288 list<VirtualSystemDescriptionEntry>::iterator it;
2289 size_t i = 0;
2290 for (it = m->descriptions.begin();
2291 it != m->descriptions.end();
2292 ++it, ++i)
2293 {
2294 VirtualSystemDescriptionEntry& vsde = *it;
2295
2296 if (aEnabled[i])
2297 {
2298 vsde.strConfig = aConfigValues[i];
2299 vsde.strExtraConfig = aExtraConfigValues[i];
2300 }
2301 else
2302 vsde.type = VirtualSystemDescriptionType_Ignore;
2303 }
2304
2305 return S_OK;
2306}
2307
2308STDMETHODIMP VirtualSystemDescription::GetWarnings(ComSafeArrayOut(BSTR, aWarnings))
2309{
2310 if (ComSafeArrayOutIsNull(aWarnings))
2311 return E_POINTER;
2312
2313 AutoCaller autoCaller(this);
2314 CheckComRCReturnRC(autoCaller.rc());
2315
2316 AutoReadLock alock(this);
2317
2318 com::SafeArray<BSTR> sfaWarnings(m->warnings.size());
2319
2320 list<Utf8Str>::const_iterator it;
2321 size_t i = 0;
2322 for (it = m->warnings.begin();
2323 it != m->warnings.end();
2324 ++it, ++i)
2325 {
2326 Bstr bstr = *it;
2327 bstr.cloneTo(&sfaWarnings[i]);
2328 }
2329
2330 sfaWarnings.detachTo(ComSafeArrayOutArg(aWarnings));
2331
2332 return S_OK;
2333}
2334
2335void VirtualSystemDescription::addEntry(VirtualSystemDescriptionType_T aType,
2336 const Utf8Str &strRef,
2337 const Utf8Str &aOrigValue,
2338 const Utf8Str &aAutoValue,
2339 const Utf8Str &strExtraConfig /*= ""*/)
2340{
2341 VirtualSystemDescriptionEntry vsde;
2342 vsde.ulIndex = m->descriptions.size(); // each entry gets an index so the client side can reference them
2343 vsde.type = aType;
2344 vsde.strRef = strRef;
2345 vsde.strOrig = aOrigValue;
2346 vsde.strConfig = aAutoValue;
2347 vsde.strExtraConfig = strExtraConfig;
2348
2349 m->descriptions.push_back(vsde);
2350}
2351
2352void VirtualSystemDescription::addWarning(const char* aWarning, ...)
2353{
2354 va_list args;
2355 va_start(args, aWarning);
2356 Utf8StrFmtVA str(aWarning, args);
2357 va_end(args);
2358 m->warnings.push_back(str);
2359}
2360
2361std::list<VirtualSystemDescriptionEntry*> VirtualSystemDescription::findByType(VirtualSystemDescriptionType_T aType)
2362{
2363 std::list<VirtualSystemDescriptionEntry*> vsd;
2364
2365 list<VirtualSystemDescriptionEntry>::iterator it;
2366 for (it = m->descriptions.begin();
2367 it != m->descriptions.end();
2368 ++it)
2369 {
2370 if (it->type == aType)
2371 vsd.push_back(&(*it));
2372 }
2373
2374 return vsd;
2375}
2376
2377const VirtualSystemDescriptionEntry* VirtualSystemDescription::findControllerFromID(uint32_t id)
2378{
2379 Utf8Str strRef = Utf8StrFmt("%RI32", id);
2380 list<VirtualSystemDescriptionEntry>::const_iterator it;
2381 for (it = m->descriptions.begin();
2382 it != m->descriptions.end();
2383 ++it)
2384 {
2385 switch (it->type)
2386 {
2387 case VirtualSystemDescriptionType_HardDiskControllerIDE:
2388 case VirtualSystemDescriptionType_HardDiskControllerSATA:
2389 case VirtualSystemDescriptionType_HardDiskControllerSCSI:
2390 if (it->strRef == strRef)
2391 return &(*it);
2392 break;
2393 }
2394 }
2395
2396 return NULL;
2397}
2398
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