VirtualBox

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

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

OVF: nuke CHECK_ERROR_THROW macro again, go back to throwing HRESULT

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 102.1 KB
Line 
1/* $Id: ApplianceImpl.cpp 16944 2009-02-19 12:46:11Z 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
1653struct MyHardDiskAttachment
1654{
1655 Guid uuid;
1656 ComPtr<IMachine> pMachine;
1657 StorageBus_T busType;
1658 int32_t lChannel;
1659 int32_t lDevice;
1660};
1661
1662/* static */
1663DECLCALLBACK(int) Appliance::taskThread(RTTHREAD aThread, void *pvUser)
1664{
1665 std::auto_ptr<Task> task(static_cast<Task *>(pvUser));
1666 AssertReturn(task.get(), VERR_GENERAL_FAILURE);
1667
1668 Appliance *app = task->that;
1669
1670 /// @todo ugly hack, fix ComAssert... (same as in HardDisk::taskThread)
1671// #define setError app->setError
1672
1673 LogFlowFuncEnter();
1674 LogFlowFunc(("Appliance %p\n", app));
1675
1676 AutoCaller autoCaller(app);
1677 CheckComRCReturnRC(autoCaller.rc());
1678
1679 AutoWriteLock appLock(app);
1680
1681 HRESULT rc = S_OK;
1682
1683 ComPtr<IVirtualBox> pVirtualBox(app->mVirtualBox);
1684
1685 // rollback for errors:
1686 // 1) a list of images that we created/imported
1687 list<MyHardDiskAttachment> llHardDiskAttachments;
1688 list< ComPtr<IHardDisk> > llHardDisksCreated;
1689 list<Guid> llMachinesRegistered;
1690
1691 ComPtr<ISession> session;
1692 bool fSessionOpen = false;
1693 rc = session.createInprocObject(CLSID_Session);
1694 CheckComRCReturnRC(rc);
1695
1696 list<VirtualSystem>::const_iterator it;
1697 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it1;
1698 /* Iterate through all virtual systems of that appliance */
1699 size_t i = 0;
1700 for (it = app->m->llVirtualSystems.begin(),
1701 it1 = app->m->virtualSystemDescriptions.begin();
1702 it != app->m->llVirtualSystems.end();
1703 ++it, ++it1, ++i)
1704 {
1705 const VirtualSystem &vsysThis = *it;
1706 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it1);
1707
1708 ComPtr<IMachine> pNewMachine;
1709
1710 /* Catch possible errors */
1711 try
1712 {
1713 if (!task->progress.isNull())
1714 task->progress->advanceOperation(BstrFmt(tr("Importing Virtual System %d"), i + 1));
1715
1716 /* How many sub notifications are necessary? */
1717 const float opCountMax = 100.0/5;
1718 uint32_t opCount = 0;
1719
1720 /* Guest OS type */
1721 std::list<VirtualSystemDescriptionEntry*> vsdeOS;
1722 vsdeOS = vsdescThis->findByType(VirtualSystemDescriptionType_OS);
1723 if (vsdeOS.size() < 1)
1724 throw setError(VBOX_E_FILE_ERROR,
1725 tr("Missing guest OS type"));
1726 const Utf8Str &strOsTypeVBox = vsdeOS.front()->strConfig;
1727
1728 /* Now that we know the base system get our internal defaults based on that. */
1729 ComPtr<IGuestOSType> osType;
1730 rc = pVirtualBox->GetGuestOSType(Bstr(strOsTypeVBox), osType.asOutParam());
1731 if (rc) throw rc;
1732
1733 /* Create the machine */
1734 /* First get the name */
1735 std::list<VirtualSystemDescriptionEntry*> vsdeName = vsdescThis->findByType(VirtualSystemDescriptionType_Name);
1736 if (vsdeName.size() < 1)
1737 throw setError(VBOX_E_FILE_ERROR,
1738 tr("Missing VM name"));
1739 const Utf8Str &strNameVBox = vsdeName.front()->strConfig;
1740 rc = pVirtualBox->CreateMachine(Bstr(strNameVBox), Bstr(strOsTypeVBox),
1741 Bstr(), Guid(),
1742 pNewMachine.asOutParam());
1743 if (rc) throw rc;
1744
1745 if (!task->progress.isNull())
1746 rc = task->progress->notifyProgress((uint32_t)(opCountMax * opCount++));
1747
1748 /* CPU count (ignored for now) */
1749 // EntriesList vsdeCPU = vsd->findByType (VirtualSystemDescriptionType_CPU);
1750
1751 /* RAM */
1752 std::list<VirtualSystemDescriptionEntry*> vsdeRAM = vsdescThis->findByType(VirtualSystemDescriptionType_Memory);
1753 ComAssertMsgThrow(vsdeRAM.size() == 1, ("RAM size missing"), E_FAIL);
1754 const Utf8Str &memoryVBox = vsdeRAM.front()->strConfig;
1755 ULONG tt = (ULONG)RTStrToUInt64(memoryVBox.c_str());
1756 rc = pNewMachine->COMSETTER(MemorySize)(tt);
1757 if (rc) throw rc;
1758
1759 /* VRAM */
1760 /* Get the recommended VRAM for this guest OS type */
1761 ULONG vramVBox;
1762 rc = osType->COMGETTER(RecommendedVRAM)(&vramVBox);
1763 if (rc) throw rc;
1764
1765 /* Set the VRAM */
1766 rc = pNewMachine->COMSETTER(VRAMSize)(vramVBox);
1767 if (rc) throw rc;
1768
1769 if (!task->progress.isNull())
1770 task->progress->notifyProgress((uint32_t)(opCountMax * opCount++));
1771
1772 /* Audio Adapter */
1773 std::list<VirtualSystemDescriptionEntry*> vsdeAudioAdapter = vsdescThis->findByType(VirtualSystemDescriptionType_SoundCard);
1774 /* @todo: we support one audio adapter only */
1775 if (vsdeAudioAdapter.size() > 0)
1776 {
1777 const Utf8Str& audioAdapterVBox = vsdeAudioAdapter.front()->strConfig;
1778 if (RTStrICmp(audioAdapterVBox, "null") != 0)
1779 {
1780 uint32_t audio = RTStrToUInt32(audioAdapterVBox.c_str());
1781 ComPtr<IAudioAdapter> audioAdapter;
1782 rc = pNewMachine->COMGETTER(AudioAdapter)(audioAdapter.asOutParam());
1783 if (rc) throw rc;
1784 rc = audioAdapter->COMSETTER(Enabled)(true);
1785 if (rc) throw rc;
1786 rc = audioAdapter->COMSETTER(AudioController)(static_cast<AudioControllerType_T>(audio));
1787 if (rc) throw rc;
1788 }
1789 }
1790
1791 /* USB Controller */
1792 std::list<VirtualSystemDescriptionEntry*> vsdeUSBController = vsdescThis->findByType(VirtualSystemDescriptionType_USBController);
1793 // USB support is enabled if there's at least one such entry; to disable USB support,
1794 // the type of the USB item would have been changed to "ignore"
1795 bool fUSBEnabled = vsdeUSBController.size() > 0;
1796
1797 ComPtr<IUSBController> usbController;
1798 rc = pNewMachine->COMGETTER(USBController)(usbController.asOutParam());
1799 if (rc) throw rc;
1800 rc = usbController->COMSETTER(Enabled)(fUSBEnabled);
1801 if (rc) throw rc;
1802
1803 if (!task->progress.isNull())
1804 task->progress->notifyProgress((uint32_t)(opCountMax * opCount++));
1805
1806 /* Change the network adapters */
1807 std::list<VirtualSystemDescriptionEntry*> vsdeNW = vsdescThis->findByType(VirtualSystemDescriptionType_NetworkAdapter);
1808 if (vsdeNW.size() == 0)
1809 {
1810 /* No network adapters, so we have to disable our default one */
1811 ComPtr<INetworkAdapter> nwVBox;
1812 rc = pNewMachine->GetNetworkAdapter(0, nwVBox.asOutParam());
1813 if (rc) throw rc;
1814 rc = nwVBox->COMSETTER(Enabled)(false);
1815 if (rc) throw rc;
1816 }
1817 else
1818 {
1819 list<VirtualSystemDescriptionEntry*>::const_iterator nwIt;
1820 /* Iterate through all network cards. We support 8 network adapters
1821 * at the maximum. (@todo: warn if there are more!) */
1822 size_t a = 0;
1823 for (nwIt = vsdeNW.begin();
1824 (nwIt != vsdeNW.end() && a < SchemaDefs::NetworkAdapterCount);
1825 ++nwIt, ++a)
1826 {
1827 const Utf8Str &nwTypeVBox = (*nwIt)->strConfig;
1828 uint32_t tt1 = RTStrToUInt32(nwTypeVBox.c_str());
1829 ComPtr<INetworkAdapter> nwVBox;
1830 rc = pNewMachine->GetNetworkAdapter((ULONG)a, nwVBox.asOutParam());
1831 if (rc) throw rc;
1832 /* Enable the network card & set the adapter type */
1833 /* NAT is set as default */
1834 rc = nwVBox->COMSETTER(Enabled)(true);
1835 if (rc) throw rc;
1836 rc = nwVBox->COMSETTER(AdapterType)(static_cast<NetworkAdapterType_T>(tt1));
1837 if (rc) throw rc;
1838 }
1839 }
1840
1841 /* Floppy drive */
1842 std::list<VirtualSystemDescriptionEntry*> vsdeFloppy = vsdescThis->findByType(VirtualSystemDescriptionType_Floppy);
1843 // Floppy support is enabled if there's at least one such entry; to disable floppy support,
1844 // the type of the floppy item would have been changed to "ignore"
1845 bool fFloppyEnabled = vsdeFloppy.size() > 0;
1846 ComPtr<IFloppyDrive> floppyDrive;
1847 rc = pNewMachine->COMGETTER(FloppyDrive)(floppyDrive.asOutParam());
1848 if (rc) throw rc;
1849 rc = floppyDrive->COMSETTER(Enabled)(fFloppyEnabled);
1850 if (rc) throw rc;
1851
1852 if (!task->progress.isNull())
1853 task->progress->notifyProgress((uint32_t)(opCountMax * opCount++));
1854
1855 /* CDROM drive */
1856 /* @todo: I can't disable the CDROM. So nothing to do for now */
1857 // std::list<VirtualSystemDescriptionEntry*> vsdeFloppy = vsd->findByType(VirtualSystemDescriptionType_CDROM);
1858
1859 /* Hard disk controller IDE */
1860 std::list<VirtualSystemDescriptionEntry*> vsdeHDCIDE = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerIDE);
1861 /* @todo: we support one IDE controller only */
1862 if (vsdeHDCIDE.size() > 0)
1863 {
1864 /* Set the appropriate IDE controller in the virtual BIOS of the VM */
1865 ComPtr<IBIOSSettings> biosSettings;
1866 rc = pNewMachine->COMGETTER(BIOSSettings)(biosSettings.asOutParam());
1867 if (rc) throw rc;
1868
1869 const char *pcszIDEType = vsdeHDCIDE.front()->strConfig.c_str();
1870 if (!strcmp(pcszIDEType, "PIIX3"))
1871 rc = biosSettings->COMSETTER(IDEControllerType)(IDEControllerType_PIIX3);
1872 else if (!strcmp(pcszIDEType, "PIIX4"))
1873 rc = biosSettings->COMSETTER(IDEControllerType)(IDEControllerType_PIIX4);
1874 else
1875 throw setError(VBOX_E_FILE_ERROR,
1876 tr("Invalid IDE controller type \"%s\""),
1877 pcszIDEType);
1878 if (rc) throw rc;
1879 }
1880#ifdef VBOX_WITH_AHCI
1881 /* Hard disk controller SATA */
1882 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSATA = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSATA);
1883 /* @todo: we support one SATA controller only */
1884 if (vsdeHDCSATA.size() > 0)
1885 {
1886 const Utf8Str &hdcVBox = vsdeHDCIDE.front()->strConfig;
1887 if (hdcVBox == "AHCI")
1888 {
1889 /* For now we have just to enable the AHCI controller. */
1890 ComPtr<ISATAController> hdcSATAVBox;
1891 rc = pNewMachine->COMGETTER(SATAController)(hdcSATAVBox.asOutParam());
1892 if (rc) throw rc;
1893 rc = hdcSATAVBox->COMSETTER(Enabled)(true);
1894 if (rc) throw rc;
1895 }
1896 else
1897 {
1898 throw setError(VBOX_E_FILE_ERROR,
1899 tr("Invalid SATA controller type \"%s\""),
1900 hdcVBox.c_str());
1901 }
1902 }
1903#endif /* VBOX_WITH_AHCI */
1904
1905 /* Hard disk controller SCSI */
1906 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSCSI = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSCSI);
1907 /* @todo: do we support more than one SCSI controller? */
1908 if (vsdeHDCSCSI.size() > 0)
1909 {
1910 /* @todo: revisit when Main support for SCSI is ready */
1911 }
1912
1913 /* Now its time to register the machine before we add any hard disks */
1914 rc = pVirtualBox->RegisterMachine(pNewMachine);
1915 if (rc) throw rc;
1916
1917 Guid newMachineId;
1918 rc = pNewMachine->COMGETTER(Id)(newMachineId.asOutParam());
1919 if (rc) throw rc;
1920
1921 if (!task->progress.isNull())
1922 task->progress->notifyProgress((uint32_t)(opCountMax * opCount++));
1923
1924 // store new machine for roll-back in case of errors
1925 llMachinesRegistered.push_back(newMachineId);
1926
1927 /* Create the hard disks & connect them to the appropriate controllers. */
1928 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
1929 if (avsdeHDs.size() > 0)
1930 {
1931 /* If in the next block an error occur we have to deregister
1932 the machine, so make an extra try/catch block. */
1933 ComPtr<IHardDisk> srcHdVBox;
1934 bool fSourceHdNeedsClosing = false;
1935
1936 try
1937 {
1938 /* In order to attach hard disks we need to open a session
1939 * for the new machine */
1940 rc = pVirtualBox->OpenSession(session, newMachineId);
1941 if (rc) throw rc;
1942 fSessionOpen = true;
1943
1944 int result;
1945 /* The disk image has to be on the same place as the OVF file. So
1946 * strip the filename out of the full file path. */
1947 Utf8Str strSrcDir = stripFilename(Utf8Str(app->m->bstrPath).raw());
1948
1949 /* Iterate over all given disk images */
1950 list<VirtualSystemDescriptionEntry*>::const_iterator itHD;
1951 for (itHD = avsdeHDs.begin();
1952 itHD != avsdeHDs.end();
1953 ++itHD)
1954 {
1955 VirtualSystemDescriptionEntry *vsdeHD = *itHD;
1956
1957 const char *pcszDstFilePath = vsdeHD->strConfig.c_str();
1958 /* Check if the destination file exists already or the
1959 * destination path is empty. */
1960 if ( !(*pcszDstFilePath)
1961 || RTPathExists(pcszDstFilePath)
1962 )
1963 /* This isn't allowed */
1964 throw setError(VBOX_E_FILE_ERROR,
1965 tr("Destination file '%s' exists",
1966 pcszDstFilePath));
1967
1968 /* Find the disk from the OVF's disk list */
1969 DiskImagesMap::const_iterator itDiskImage = app->m->mapDisks.find(vsdeHD->strRef);
1970 /* vsdeHD->strRef contains the disk identifier (e.g. "vmdisk1"), which should exist
1971 in the virtual system's disks map under that ID and also in the global images map. */
1972 VirtualDisksMap::const_iterator itVirtualDisk = vsysThis.mapVirtualDisks.find(vsdeHD->strRef);
1973
1974 if ( itDiskImage == app->m->mapDisks.end()
1975 || itVirtualDisk == vsysThis.mapVirtualDisks.end()
1976 )
1977 throw setError(E_FAIL,
1978 tr("Internal inconsistency looking up disk images."));
1979
1980 const DiskImage &di = itDiskImage->second;
1981 const VirtualDisk &vd = itVirtualDisk->second;
1982
1983 /* Make sure all target directories exists */
1984 rc = VirtualBox::ensureFilePathExists(pcszDstFilePath);
1985 if (FAILED(rc))
1986 throw rc;
1987
1988 ComPtr<IProgress> progress;
1989
1990 ComPtr<IHardDisk> dstHdVBox;
1991 /* If strHref is empty we have to create a new file */
1992 if (di.strHref.c_str()[0] == 0)
1993 {
1994 /* Which format to use? */
1995 Bstr srcFormat = L"VDI";
1996 if ( (!RTStrICmp(di.strFormat.c_str(), "http://www.vmware.com/specifications/vmdk.html#sparse"))
1997 || (!RTStrICmp(di.strFormat.c_str(), "http://www.vmware.com/specifications/vmdk.html#compressed")))
1998 srcFormat = L"VMDK";
1999 /* Create an empty hard disk */
2000 rc = pVirtualBox->CreateHardDisk(srcFormat, Bstr(pcszDstFilePath), dstHdVBox.asOutParam());
2001 if (rc) throw rc;
2002
2003 /* Create a dynamic growing disk image with the given capacity */
2004 ComPtr<IProgress> progress;
2005 rc = dstHdVBox->CreateDynamicStorage(di.iCapacity / _1M, progress.asOutParam());
2006 if (rc) throw rc;
2007
2008 /* Advance to the next operation */
2009 if (!task->progress.isNull())
2010 task->progress->advanceOperation (BstrFmt(tr("Creating virtual disk image '%s'"), pcszDstFilePath));
2011 }
2012 else
2013 {
2014 /* Construct the source file path */
2015 Utf8StrFmt strSrcFilePath("%s%c%s", strSrcDir.c_str(), RTPATH_DELIMITER, di.strHref.c_str());
2016 /* Check if the source file exists */
2017 if (!RTPathExists(strSrcFilePath.c_str()))
2018 /* This isn't allowed */
2019 throw setError(VBOX_E_FILE_ERROR,
2020 tr("Source virtual disk image file '%s' doesn't exist"),
2021 strSrcFilePath.c_str());
2022
2023 /* Clone the disk image (this is necessary cause the id has
2024 * to be recreated for the case the same hard disk is
2025 * attached already from a previous import) */
2026
2027 /* First open the existing disk image */
2028 rc = pVirtualBox->OpenHardDisk(Bstr(strSrcFilePath), srcHdVBox.asOutParam());
2029 if (rc) throw rc;
2030 fSourceHdNeedsClosing = true;
2031
2032 /* We need the format description of the source disk image */
2033 Bstr srcFormat;
2034 rc = srcHdVBox->COMGETTER(Format)(srcFormat.asOutParam());
2035 if (rc) throw rc;
2036 /* Create a new hard disk interface for the destination disk image */
2037 rc = pVirtualBox->CreateHardDisk(srcFormat, Bstr(pcszDstFilePath), dstHdVBox.asOutParam());
2038 if (rc) throw rc;
2039 /* Clone the source disk image */
2040 rc = srcHdVBox->CloneTo(dstHdVBox, progress.asOutParam());
2041 if (rc) throw rc;
2042
2043 /* Advance to the next operation */
2044 if (!task->progress.isNull())
2045 task->progress->advanceOperation (BstrFmt(tr("Importing virtual disk image '%s'"), strSrcFilePath.c_str()));
2046 }
2047
2048 // now loop until the asynchronous operation completes and then
2049 // report its result
2050 BOOL fCompleted;
2051 LONG currentPercent;
2052 while (SUCCEEDED(progress->COMGETTER(Completed(&fCompleted))))
2053 {
2054 rc = progress->COMGETTER(Percent(&currentPercent));
2055 if (rc) throw rc;
2056 if (!task->progress.isNull())
2057 task->progress->notifyProgress(currentPercent);
2058 if (fCompleted)
2059 break;
2060 /* Make sure the loop is not too tight */
2061 rc = progress->WaitForCompletion(100);
2062 if (rc) throw rc;
2063 }
2064 // report result of asynchronous operation
2065 HRESULT vrc;
2066 rc = progress->COMGETTER(ResultCode)(&vrc);
2067 if (rc) throw rc;
2068
2069 // if the thread of the progress object has an error, then
2070 // retrieve the error info from there, or it'll be lost
2071 if (FAILED(vrc))
2072 {
2073 com::ErrorInfo info(progress);
2074 const char *pcsz = Utf8Str(info.getText()).c_str();
2075 HRESULT rc2 = setError(vrc,
2076 pcsz);
2077 throw rc2;
2078 }
2079
2080 if (fSourceHdNeedsClosing)
2081 {
2082 rc = srcHdVBox->Close();
2083 if (rc) throw rc;
2084 fSourceHdNeedsClosing = false;
2085 }
2086
2087 llHardDisksCreated.push_back(dstHdVBox);
2088
2089 /* Now use the new uuid to attach the disk image to our new machine */
2090 ComPtr<IMachine> sMachine;
2091 rc = session->COMGETTER(Machine)(sMachine.asOutParam());
2092 if (rc) throw rc;
2093 Guid hdId;
2094 rc = dstHdVBox->COMGETTER(Id)(hdId.asOutParam());
2095 if (rc) throw rc;
2096
2097 /* For now we assume we have one controller of every type only */
2098 HardDiskController hdc = (*vsysThis.mapControllers.find(vd.idController)).second;
2099
2100 // this is for rollback later
2101 MyHardDiskAttachment mhda;
2102 mhda.uuid = newMachineId;
2103 mhda.pMachine = pNewMachine;
2104 mhda.busType = StorageBus_IDE;
2105 mhda.lChannel = (long)hdc.ulBusNumber; // ?!? @todo this is still wrong
2106 mhda.lDevice = (long)0; // ?!? @todo this is still wrong
2107
2108 switch (hdc.system)
2109 {
2110 case HardDiskController::IDE: mhda.busType = StorageBus_IDE; break;
2111 case HardDiskController::SATA: mhda.busType = StorageBus_SATA; break;
2112 //case HardDiskController::SCSI: sbt = StorageBus_SCSI; break; // @todo: not available yet
2113 default: break;
2114 }
2115
2116 rc = sMachine->AttachHardDisk(hdId,
2117 mhda.busType,
2118 mhda.lChannel,
2119 mhda.lDevice);
2120 if (rc) throw rc;
2121
2122 llHardDiskAttachments.push_back(mhda);
2123
2124 rc = sMachine->SaveSettings();
2125 if (rc) throw rc;
2126 } // end for (itHD = avsdeHDs.begin();
2127
2128 // only now that we're done with all disks, close the session
2129 rc = session->Close();
2130 if (rc) throw rc;
2131 fSessionOpen = false;
2132 }
2133 catch(HRESULT aRC)
2134 {
2135 if (fSourceHdNeedsClosing)
2136 srcHdVBox->Close();
2137
2138 if (fSessionOpen)
2139 session->Close();
2140
2141 throw;
2142 }
2143 }
2144 }
2145 catch(HRESULT aRC)
2146 {
2147 rc = aRC;
2148 }
2149
2150 if (FAILED(rc))
2151 break;
2152
2153 } // for (it = app->m->llVirtualSystems.begin(),
2154
2155 if (FAILED(rc))
2156 {
2157 // with _whatever_ error we've had, do a complete roll-back of
2158 // machines and disks we've created; unfortunately this is
2159 // not so trivially done...
2160
2161 HRESULT rc2;
2162 // detach all hard disks from all machines we created
2163 list<MyHardDiskAttachment>::iterator itM;
2164 for (itM = llHardDiskAttachments.begin();
2165 itM != llHardDiskAttachments.end();
2166 ++itM)
2167 {
2168 const MyHardDiskAttachment &mhda = *itM;
2169 rc2 = pVirtualBox->OpenSession(session, mhda.uuid);
2170 if (SUCCEEDED(rc2))
2171 {
2172 ComPtr<IMachine> sMachine;
2173 rc2 = session->COMGETTER(Machine)(sMachine.asOutParam());
2174 if (SUCCEEDED(rc2))
2175 {
2176 rc2 = sMachine->DetachHardDisk(mhda.busType, mhda.lChannel, mhda.lDevice);
2177 rc2 = sMachine->SaveSettings();
2178 }
2179 session->Close();
2180 }
2181 }
2182
2183 // now clean up all hard disks we created
2184 list< ComPtr<IHardDisk> >::iterator itHD;
2185 for (itHD = llHardDisksCreated.begin();
2186 itHD != llHardDisksCreated.end();
2187 ++itHD)
2188 {
2189 ComPtr<IHardDisk> pDisk = *itHD;
2190 ComPtr<IProgress> pProgress;
2191 rc2 = pDisk->DeleteStorage(pProgress.asOutParam());
2192 rc2 = pProgress->WaitForCompletion(-1);
2193 }
2194
2195 // finally, deregister and remove all machines
2196 list<Guid>::iterator itID;
2197 for (itID = llMachinesRegistered.begin();
2198 itID != llMachinesRegistered.end();
2199 ++itID)
2200 {
2201 const Guid &guid = *itID;
2202 ComPtr<IMachine> failedMachine;
2203 rc2 = pVirtualBox->UnregisterMachine(guid, failedMachine.asOutParam());
2204 if (SUCCEEDED(rc2))
2205 rc2 = failedMachine->DeleteSettings();
2206 }
2207 }
2208
2209 task->rc = rc;
2210
2211 if (!task->progress.isNull())
2212 task->progress->notifyComplete(rc);
2213
2214 LogFlowFunc(("rc=%Rhrc\n", rc));
2215 LogFlowFuncLeave();
2216
2217 return VINF_SUCCESS;
2218}
2219
2220// IVirtualSystemDescription constructor / destructor
2221////////////////////////////////////////////////////////////////////////////////
2222
2223DEFINE_EMPTY_CTOR_DTOR(VirtualSystemDescription)
2224struct shutup3 {};
2225
2226struct VirtualSystemDescription::Data
2227{
2228 list<VirtualSystemDescriptionEntry> descriptions;
2229 list<Utf8Str> warnings;
2230};
2231
2232HRESULT VirtualSystemDescription::init()
2233{
2234 /* Enclose the state transition NotReady->InInit->Ready */
2235 AutoInitSpan autoInitSpan(this);
2236 AssertReturn(autoInitSpan.isOk(), E_FAIL);
2237
2238 /* Initialize data */
2239 m = new Data();
2240
2241 /* Confirm a successful initialization */
2242 autoInitSpan.setSucceeded();
2243
2244 return S_OK;
2245}
2246
2247void VirtualSystemDescription::uninit()
2248{
2249 delete m;
2250 m = NULL;
2251}
2252
2253STDMETHODIMP VirtualSystemDescription::COMGETTER(Count)(ULONG *aCount)
2254{
2255 if (!aCount)
2256 return E_POINTER;
2257
2258 AutoCaller autoCaller(this);
2259 CheckComRCReturnRC(autoCaller.rc());
2260
2261 AutoReadLock alock(this);
2262
2263 *aCount = (ULONG)m->descriptions.size();
2264
2265 return S_OK;
2266}
2267
2268STDMETHODIMP VirtualSystemDescription::GetDescription(ComSafeArrayOut(VirtualSystemDescriptionType_T, aTypes),
2269 ComSafeArrayOut(BSTR, aRefs),
2270 ComSafeArrayOut(BSTR, aOrigValues),
2271 ComSafeArrayOut(BSTR, aConfigValues),
2272 ComSafeArrayOut(BSTR, aExtraConfigValues))
2273{
2274 if (ComSafeArrayOutIsNull(aTypes) ||
2275 ComSafeArrayOutIsNull(aRefs) ||
2276 ComSafeArrayOutIsNull(aOrigValues) ||
2277 ComSafeArrayOutIsNull(aConfigValues) ||
2278 ComSafeArrayOutIsNull(aExtraConfigValues))
2279 return E_POINTER;
2280
2281 AutoCaller autoCaller(this);
2282 CheckComRCReturnRC(autoCaller.rc());
2283
2284 AutoReadLock alock(this);
2285
2286 ULONG c = (ULONG)m->descriptions.size();
2287 com::SafeArray<VirtualSystemDescriptionType_T> sfaTypes(c);
2288 com::SafeArray<BSTR> sfaRefs(c);
2289 com::SafeArray<BSTR> sfaOrigValues(c);
2290 com::SafeArray<BSTR> sfaConfigValues(c);
2291 com::SafeArray<BSTR> sfaExtraConfigValues(c);
2292
2293 list<VirtualSystemDescriptionEntry>::const_iterator it;
2294 size_t i = 0;
2295 for (it = m->descriptions.begin();
2296 it != m->descriptions.end();
2297 ++it, ++i)
2298 {
2299 const VirtualSystemDescriptionEntry &vsde = (*it);
2300
2301 sfaTypes[i] = vsde.type;
2302
2303 Bstr bstr = vsde.strRef;
2304 bstr.cloneTo(&sfaRefs[i]);
2305
2306 bstr = vsde.strOrig;
2307 bstr.cloneTo(&sfaOrigValues[i]);
2308
2309 bstr = vsde.strConfig;
2310 bstr.cloneTo(&sfaConfigValues[i]);
2311
2312 bstr = vsde.strExtraConfig;
2313 bstr.cloneTo(&sfaExtraConfigValues[i]);
2314 }
2315
2316 sfaTypes.detachTo(ComSafeArrayOutArg(aTypes));
2317 sfaRefs.detachTo(ComSafeArrayOutArg(aRefs));
2318 sfaOrigValues.detachTo(ComSafeArrayOutArg(aOrigValues));
2319 sfaConfigValues.detachTo(ComSafeArrayOutArg(aConfigValues));
2320 sfaExtraConfigValues.detachTo(ComSafeArrayOutArg(aExtraConfigValues));
2321
2322 return S_OK;
2323}
2324
2325STDMETHODIMP VirtualSystemDescription::SetFinalValues(ComSafeArrayIn(BOOL, aEnabled),
2326 ComSafeArrayIn(IN_BSTR, argConfigValues),
2327 ComSafeArrayIn(IN_BSTR, argExtraConfigValues))
2328{
2329 CheckComArgSafeArrayNotNull(argConfigValues);
2330 CheckComArgSafeArrayNotNull(argExtraConfigValues);
2331
2332 AutoCaller autoCaller(this);
2333 CheckComRCReturnRC(autoCaller.rc());
2334
2335 AutoWriteLock alock(this);
2336
2337 com::SafeArray<IN_BSTR> aConfigValues(ComSafeArrayInArg(argConfigValues));
2338 com::SafeArray<IN_BSTR> aExtraConfigValues(ComSafeArrayInArg(argExtraConfigValues));
2339
2340 if ( (aConfigValues.size() != m->descriptions.size())
2341 || (aExtraConfigValues.size() != m->descriptions.size())
2342 )
2343 return E_INVALIDARG;
2344
2345 list<VirtualSystemDescriptionEntry>::iterator it;
2346 size_t i = 0;
2347 for (it = m->descriptions.begin();
2348 it != m->descriptions.end();
2349 ++it, ++i)
2350 {
2351 VirtualSystemDescriptionEntry& vsde = *it;
2352
2353 if (aEnabled[i])
2354 {
2355 vsde.strConfig = aConfigValues[i];
2356 vsde.strExtraConfig = aExtraConfigValues[i];
2357 }
2358 else
2359 vsde.type = VirtualSystemDescriptionType_Ignore;
2360 }
2361
2362 return S_OK;
2363}
2364
2365STDMETHODIMP VirtualSystemDescription::GetWarnings(ComSafeArrayOut(BSTR, aWarnings))
2366{
2367 if (ComSafeArrayOutIsNull(aWarnings))
2368 return E_POINTER;
2369
2370 AutoCaller autoCaller(this);
2371 CheckComRCReturnRC(autoCaller.rc());
2372
2373 AutoReadLock alock(this);
2374
2375 com::SafeArray<BSTR> sfaWarnings(m->warnings.size());
2376
2377 list<Utf8Str>::const_iterator it;
2378 size_t i = 0;
2379 for (it = m->warnings.begin();
2380 it != m->warnings.end();
2381 ++it, ++i)
2382 {
2383 Bstr bstr = *it;
2384 bstr.cloneTo(&sfaWarnings[i]);
2385 }
2386
2387 sfaWarnings.detachTo(ComSafeArrayOutArg(aWarnings));
2388
2389 return S_OK;
2390}
2391
2392void VirtualSystemDescription::addEntry(VirtualSystemDescriptionType_T aType,
2393 const Utf8Str &strRef,
2394 const Utf8Str &aOrigValue,
2395 const Utf8Str &aAutoValue,
2396 const Utf8Str &strExtraConfig /*= ""*/)
2397{
2398 VirtualSystemDescriptionEntry vsde;
2399 vsde.ulIndex = m->descriptions.size(); // each entry gets an index so the client side can reference them
2400 vsde.type = aType;
2401 vsde.strRef = strRef;
2402 vsde.strOrig = aOrigValue;
2403 vsde.strConfig = aAutoValue;
2404 vsde.strExtraConfig = strExtraConfig;
2405
2406 m->descriptions.push_back(vsde);
2407}
2408
2409void VirtualSystemDescription::addWarning(const char* aWarning, ...)
2410{
2411 va_list args;
2412 va_start(args, aWarning);
2413 Utf8StrFmtVA str(aWarning, args);
2414 va_end(args);
2415 m->warnings.push_back(str);
2416}
2417
2418std::list<VirtualSystemDescriptionEntry*> VirtualSystemDescription::findByType(VirtualSystemDescriptionType_T aType)
2419{
2420 std::list<VirtualSystemDescriptionEntry*> vsd;
2421
2422 list<VirtualSystemDescriptionEntry>::iterator it;
2423 for (it = m->descriptions.begin();
2424 it != m->descriptions.end();
2425 ++it)
2426 {
2427 if (it->type == aType)
2428 vsd.push_back(&(*it));
2429 }
2430
2431 return vsd;
2432}
2433
2434const VirtualSystemDescriptionEntry* VirtualSystemDescription::findControllerFromID(uint32_t id)
2435{
2436 Utf8Str strRef = Utf8StrFmt("%RI32", id);
2437 list<VirtualSystemDescriptionEntry>::const_iterator it;
2438 for (it = m->descriptions.begin();
2439 it != m->descriptions.end();
2440 ++it)
2441 {
2442 switch (it->type)
2443 {
2444 case VirtualSystemDescriptionType_HardDiskControllerIDE:
2445 case VirtualSystemDescriptionType_HardDiskControllerSATA:
2446 case VirtualSystemDescriptionType_HardDiskControllerSCSI:
2447 if (it->strRef == strRef)
2448 return &(*it);
2449 break;
2450 }
2451 }
2452
2453 return NULL;
2454}
2455
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