VirtualBox

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

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

Fix burn; can't use NOREF() here.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 158.1 KB
Line 
1/* $Id: ApplianceImpl.cpp 17913 2009-03-16 10:53:29Z 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#include "MachineImpl.h"
33
34#include "Logging.h"
35
36#include "VBox/xml.h"
37
38using namespace std;
39
40////////////////////////////////////////////////////////////////////////////////
41//
42// hardware definitions
43//
44////////////////////////////////////////////////////////////////////////////////
45
46struct DiskImage
47{
48 Utf8Str strDiskId; // value from DiskSection/Disk/@diskId
49 int64_t iCapacity; // value from DiskSection/Disk/@capacity;
50 // (maximum size for dynamic images, I guess; we always translate this to bytes)
51 int64_t iPopulatedSize; // value from DiskSection/Disk/@populatedSize
52 // (actual used size of disk, always in bytes; can be an estimate of used disk
53 // space, but cannot be larger than iCapacity)
54 Utf8Str strFormat; // value from DiskSection/Disk/@format
55 // typically http://www.vmware.com/specifications/vmdk.html#sparse
56
57 // fields from /References/File; the spec says the file reference from disk can be empty,
58 // so in that case, strFilename will be empty, then a new disk should be created
59 Utf8Str strHref; // value from /References/File/@href (filename); if empty, then the remaining fields are ignored
60 int64_t iSize; // value from /References/File/@size (optional according to spec; then we set -1 here)
61 int64_t iChunkSize; // value from /References/File/@chunkSize (optional, unsupported)
62 Utf8Str strCompression; // value from /References/File/@compression (optional, can be "gzip" according to spec)
63};
64
65struct VirtualHardwareItem
66{
67 Utf8Str strDescription;
68 Utf8Str strCaption;
69 Utf8Str strElementName;
70
71 uint32_t ulInstanceID;
72 uint32_t ulParent;
73
74 OVFResourceType_T resourceType;
75 Utf8Str strOtherResourceType;
76 Utf8Str strResourceSubType;
77
78 Utf8Str strHostResource; // "Abstractly specifies how a device shall connect to a resource on the deployment platform.
79 // Not all devices need a backing." Used with disk items, for which this references a virtual
80 // disk from the Disks section.
81 bool fAutomaticAllocation;
82 bool fAutomaticDeallocation;
83 Utf8Str strConnection; // "All Ethernet adapters that specify the same abstract network connection name within an OVF
84 // package shall be deployed on the same network. The abstract network connection name shall be
85 // listed in the NetworkSection at the outermost envelope level." We ignore this and only set up
86 // a network adapter depending on the network name.
87 Utf8Str strAddress; // "Device-specific. For an Ethernet adapter, this specifies the MAC address."
88 Utf8Str strAddressOnParent; // "For a device, this specifies its location on the controller."
89 Utf8Str strAllocationUnits; // "Specifies the units of allocation used. For example, “byte * 2^20”."
90 uint64_t ullVirtualQuantity; // "Specifies the quantity of resources presented. For example, “256”."
91 uint64_t ullReservation; // "Specifies the minimum quantity of resources guaranteed to be available."
92 uint64_t ullLimit; // "Specifies the maximum quantity of resources that will be granted."
93 uint64_t ullWeight; // "Specifies a relative priority for this allocation in relation to other allocations."
94
95 Utf8Str strConsumerVisibility;
96 Utf8Str strMappingBehavior;
97 Utf8Str strPoolID;
98 uint32_t ulBusNumber; // seen with IDE controllers, but not listed in OVF spec
99
100 uint32_t ulLineNumber; // line number of <Item> element in XML source; cached for error messages
101
102 VirtualHardwareItem()
103 : ulInstanceID(0), fAutomaticAllocation(false), fAutomaticDeallocation(false), ullVirtualQuantity(0), ullReservation(0), ullLimit(0), ullWeight(0), ulBusNumber(0), ulLineNumber(0)
104 {};
105};
106
107typedef map<Utf8Str, DiskImage> DiskImagesMap;
108
109struct VirtualSystem;
110
111typedef map<uint32_t, VirtualHardwareItem> HardwareItemsMap;
112
113struct HardDiskController
114{
115 uint32_t idController; // instance ID (Item/InstanceId); this gets referenced from HardDisk
116 enum ControllerSystemType { IDE, SATA, SCSI };
117 ControllerSystemType system; // one of IDE, SATA, SCSI
118 Utf8Str strControllerType; // controller subtype (Item/ResourceSubType); e.g. "LsiLogic"; can be empty (esp. for IDE)
119 Utf8Str strAddress; // for IDE
120 uint32_t ulBusNumber; // for IDE
121
122 HardDiskController()
123 : idController(0),
124 ulBusNumber(0)
125 {
126 }
127};
128
129typedef map<uint32_t, HardDiskController> ControllersMap;
130
131struct VirtualDisk
132{
133 uint32_t idController; // SCSI (or IDE) controller this disk is connected to;
134 // points into VirtualSystem.mapControllers
135 uint32_t ulAddressOnParent; // parsed strAddressOnParent of hardware item; will be 0 or 1 for IDE
136 // and possibly higher for disks attached to SCSI controllers (untested)
137 Utf8Str strDiskId; // if the hard disk has an ovf:/disk/<id> reference,
138 // this receives the <id> component; points to one of the
139 // references in Appliance::Data.mapDisks
140};
141
142typedef map<Utf8Str, VirtualDisk> VirtualDisksMap;
143
144struct VirtualSystem
145{
146 Utf8Str strName; // copy of VirtualSystem/@id
147
148 CIMOSType_T cimos;
149 Utf8Str strVirtualSystemType; // generic hardware description; OVF says this can be something like "vmx-4" or "xen";
150 // VMware Workstation 6.5 is "vmx-07"
151
152 HardwareItemsMap mapHardwareItems; // map of virtual hardware items, sorted by unique instance ID
153
154 uint64_t ullMemorySize; // always in bytes, copied from llHardwareItems; default = 0 (unspecified)
155 uint16_t cCPUs; // no. of CPUs, copied from llHardwareItems; default = 1
156
157 list<Utf8Str> llNetworkNames;
158 // list of strings referring to network names
159 // (one for each VirtualSystem/Item[@ResourceType=10]/Connection element)
160
161 ControllersMap mapControllers;
162 // list of hard disk controllers
163 // (one for each VirtualSystem/Item[@ResourceType=6] element with accumulated data from children)
164
165 VirtualDisksMap mapVirtualDisks;
166 // (one for each VirtualSystem/Item[@ResourceType=17] element with accumulated data from children)
167
168 bool fHasFloppyDrive; // true if there's a floppy item in mapHardwareItems
169 bool fHasCdromDrive; // true if there's a CD-ROM item in mapHardwareItems; ISO images are not yet supported by OVFtool
170 bool fHasUsbController; // true if there's a USB controller item in mapHardwareItems
171
172 Utf8Str strSoundCardType; // if not empty, then the system wants a soundcard; this then specifies the hardware;
173 // VMware Workstation 6.5 uses "ensoniq1371" for example
174
175 Utf8Str strLicenceInfo; // license info if any; receives contents of VirtualSystem/EulaSection/Info
176 Utf8Str strLicenceText; // license info if any; receives contents of VirtualSystem/EulaSection/License
177
178 VirtualSystem()
179 : ullMemorySize(0), cCPUs(1), fHasFloppyDrive(false), fHasCdromDrive(false), fHasUsbController(false)
180 {
181 }
182};
183
184////////////////////////////////////////////////////////////////////////////////
185//
186// Appliance data definition
187//
188////////////////////////////////////////////////////////////////////////////////
189
190// opaque private instance data of Appliance class
191struct Appliance::Data
192{
193 Utf8Str strPath; // file name last given to either read() or write()
194
195 DiskImagesMap mapDisks; // map of DiskImage structs, sorted by DiskImage.strDiskId
196
197 list<VirtualSystem> llVirtualSystems; // list of virtual systems, created by and valid after read()
198
199 list< ComObjPtr<VirtualSystemDescription> > virtualSystemDescriptions; //
200
201 list<Utf8Str> llWarnings;
202};
203
204struct VirtualSystemDescription::Data
205{
206 list<VirtualSystemDescriptionEntry> llDescriptions;
207};
208
209////////////////////////////////////////////////////////////////////////////////
210//
211// Threads
212//
213////////////////////////////////////////////////////////////////////////////////
214
215struct Appliance::TaskImportMachines
216{
217 TaskImportMachines(Appliance *aThat, Progress *aProgress)
218 : pAppliance(aThat)
219 , progress(aProgress)
220 , rc(S_OK)
221 {}
222 ~TaskImportMachines() {}
223
224 HRESULT startThread();
225
226 Appliance *pAppliance;
227 ComObjPtr<Progress> progress;
228 HRESULT rc;
229};
230
231struct Appliance::TaskWriteOVF
232{
233 TaskWriteOVF(Appliance *aThat, Progress *aProgress)
234 : pAppliance(aThat)
235 , progress(aProgress)
236 , rc(S_OK)
237 {}
238 ~TaskWriteOVF() {}
239
240 HRESULT startThread();
241
242 Appliance *pAppliance;
243 ComObjPtr<Progress> progress;
244 HRESULT rc;
245};
246
247////////////////////////////////////////////////////////////////////////////////
248//
249// internal helpers
250//
251////////////////////////////////////////////////////////////////////////////////
252
253static Utf8Str stripFilename(const Utf8Str &strFile)
254{
255 Utf8Str str2(strFile);
256 RTPathStripFilename(str2.mutableRaw());
257 return str2;
258}
259
260static const struct
261{
262 CIMOSType_T cim;
263 const char *pcszVbox;
264}
265 g_osTypes[] =
266 {
267 { CIMOSType_CIMOS_Unknown, SchemaDefs_OSTypeId_Other },
268 { CIMOSType_CIMOS_OS2, SchemaDefs_OSTypeId_OS2 },
269 { CIMOSType_CIMOS_MSDOS, SchemaDefs_OSTypeId_DOS },
270 { CIMOSType_CIMOS_WIN3x, SchemaDefs_OSTypeId_Windows31 },
271 { CIMOSType_CIMOS_WIN95, SchemaDefs_OSTypeId_Windows95 },
272 { CIMOSType_CIMOS_WIN98, SchemaDefs_OSTypeId_Windows98 },
273 { CIMOSType_CIMOS_WINNT, SchemaDefs_OSTypeId_WindowsNT4 },
274 { CIMOSType_CIMOS_NetWare, SchemaDefs_OSTypeId_Netware },
275 { CIMOSType_CIMOS_NovellOES, SchemaDefs_OSTypeId_Netware },
276 { CIMOSType_CIMOS_Solaris, SchemaDefs_OSTypeId_Solaris },
277 { CIMOSType_CIMOS_SunOS, SchemaDefs_OSTypeId_Solaris },
278 { CIMOSType_CIMOS_FreeBSD, SchemaDefs_OSTypeId_FreeBSD },
279 { CIMOSType_CIMOS_NetBSD, SchemaDefs_OSTypeId_NetBSD },
280 { CIMOSType_CIMOS_QNX, SchemaDefs_OSTypeId_QNX },
281 { CIMOSType_CIMOS_Windows2000, SchemaDefs_OSTypeId_Windows2000 },
282 { CIMOSType_CIMOS_WindowsMe, SchemaDefs_OSTypeId_WindowsMe },
283 { CIMOSType_CIMOS_OpenBSD, SchemaDefs_OSTypeId_OpenBSD },
284 { CIMOSType_CIMOS_WindowsXP, SchemaDefs_OSTypeId_WindowsXP },
285 { CIMOSType_CIMOS_WindowsXPEmbedded, SchemaDefs_OSTypeId_WindowsXP },
286 { CIMOSType_CIMOS_WindowsEmbeddedforPointofService, SchemaDefs_OSTypeId_WindowsXP },
287 { CIMOSType_CIMOS_MicrosoftWindowsServer2003, SchemaDefs_OSTypeId_Windows2003 },
288 { CIMOSType_CIMOS_MicrosoftWindowsServer2003_64, SchemaDefs_OSTypeId_Windows2003_64 },
289 { CIMOSType_CIMOS_WindowsXP_64, SchemaDefs_OSTypeId_WindowsXP_64 },
290 { CIMOSType_CIMOS_WindowsVista, SchemaDefs_OSTypeId_WindowsVista },
291 { CIMOSType_CIMOS_WindowsVista_64, SchemaDefs_OSTypeId_WindowsVista_64 },
292 { CIMOSType_CIMOS_MicrosoftWindowsServer2008, SchemaDefs_OSTypeId_Windows2008 },
293 { CIMOSType_CIMOS_MicrosoftWindowsServer2008_64, SchemaDefs_OSTypeId_Windows2008_64 },
294 { CIMOSType_CIMOS_FreeBSD_64, SchemaDefs_OSTypeId_FreeBSD_64 },
295 { CIMOSType_CIMOS_RedHatEnterpriseLinux, SchemaDefs_OSTypeId_RedHat },
296 { CIMOSType_CIMOS_RedHatEnterpriseLinux_64, SchemaDefs_OSTypeId_RedHat_64 },
297 { CIMOSType_CIMOS_Solaris_64, SchemaDefs_OSTypeId_Solaris_64 },
298 { CIMOSType_CIMOS_SUSE, SchemaDefs_OSTypeId_OpenSUSE },
299 { CIMOSType_CIMOS_SLES, SchemaDefs_OSTypeId_OpenSUSE },
300 { CIMOSType_CIMOS_NovellLinuxDesktop, SchemaDefs_OSTypeId_OpenSUSE },
301 { CIMOSType_CIMOS_SUSE_64, SchemaDefs_OSTypeId_OpenSUSE_64 },
302 { CIMOSType_CIMOS_SLES_64, SchemaDefs_OSTypeId_OpenSUSE_64 },
303 { CIMOSType_CIMOS_LINUX, SchemaDefs_OSTypeId_Linux },
304 { CIMOSType_CIMOS_SunJavaDesktopSystem, SchemaDefs_OSTypeId_Linux },
305 { CIMOSType_CIMOS_TurboLinux, SchemaDefs_OSTypeId_Linux},
306
307 // { CIMOSType_CIMOS_TurboLinux_64, },
308 // { CIMOSType_CIMOS_Linux_64, },
309 // osTypeVBox = VBOXOSTYPE_Linux_x64;
310 // break;
311
312 { CIMOSType_CIMOS_Mandriva, SchemaDefs_OSTypeId_Mandriva },
313 { CIMOSType_CIMOS_Mandriva_64, SchemaDefs_OSTypeId_Mandriva_64 },
314 { CIMOSType_CIMOS_Ubuntu, SchemaDefs_OSTypeId_Ubuntu },
315 { CIMOSType_CIMOS_Ubuntu_64, SchemaDefs_OSTypeId_Ubuntu_64 },
316 { CIMOSType_CIMOS_Debian, SchemaDefs_OSTypeId_Debian },
317 { CIMOSType_CIMOS_Debian_64, SchemaDefs_OSTypeId_Debian_64 },
318 { CIMOSType_CIMOS_Linux_2_4_x, SchemaDefs_OSTypeId_Linux24 },
319 { CIMOSType_CIMOS_Linux_2_4_x_64, SchemaDefs_OSTypeId_Linux24_64 },
320 { CIMOSType_CIMOS_Linux_2_6_x, SchemaDefs_OSTypeId_Linux26 },
321 { CIMOSType_CIMOS_Linux_2_6_x_64, SchemaDefs_OSTypeId_Linux26_64 }
322};
323
324/**
325 * Private helper func that suggests a VirtualBox guest OS type
326 * for the given OVF operating system type.
327 * @param osTypeVBox
328 * @param c
329 */
330static void convertCIMOSType2VBoxOSType(Utf8Str &strType, CIMOSType_T c)
331{
332 for (size_t i = 0; i < RT_ELEMENTS(g_osTypes); ++i)
333 {
334 if (c == g_osTypes[i].cim)
335 {
336 strType = g_osTypes[i].pcszVbox;
337 return;
338 }
339 }
340
341 strType = SchemaDefs_OSTypeId_Other;
342}
343
344/**
345 * Private helper func that suggests a VirtualBox guest OS type
346 * for the given OVF operating system type.
347 * @param osTypeVBox
348 * @param c
349 */
350static CIMOSType_T convertVBoxOSType2CIMOSType(const char *pcszVbox)
351{
352 for (size_t i = 0; i < RT_ELEMENTS(g_osTypes); ++i)
353 {
354 if (!RTStrICmp(pcszVbox, g_osTypes[i].pcszVbox))
355 return g_osTypes[i].cim;
356 }
357
358 return CIMOSType_CIMOS_Other;
359}
360
361////////////////////////////////////////////////////////////////////////////////
362//
363// IVirtualBox public methods
364//
365////////////////////////////////////////////////////////////////////////////////
366
367// This code is here so we won't have to include the appliance headers in the
368// IVirtualBox implementation.
369
370/**
371 * Implementation for IVirtualBox::createAppliance.
372 *
373 * @param anAppliance IAppliance object created if S_OK is returned.
374 * @return S_OK or error.
375 */
376STDMETHODIMP VirtualBox::CreateAppliance(IAppliance** anAppliance)
377{
378 HRESULT rc;
379
380 ComObjPtr<Appliance> appliance;
381 appliance.createObject();
382 rc = appliance->init(this);
383
384 if (SUCCEEDED(rc))
385 appliance.queryInterfaceTo(anAppliance);
386
387 return rc;
388}
389
390////////////////////////////////////////////////////////////////////////////////
391//
392// Appliance::task methods
393//
394////////////////////////////////////////////////////////////////////////////////
395
396HRESULT Appliance::TaskImportMachines::startThread()
397{
398 int vrc = RTThreadCreate(NULL, Appliance::taskThreadImportMachines, this,
399 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
400 "Appliance::Task");
401 ComAssertMsgRCRet(vrc,
402 ("Could not create taskThreadImportMachines (%Rrc)\n", vrc), E_FAIL);
403
404 return S_OK;
405}
406
407HRESULT Appliance::TaskWriteOVF::startThread()
408{
409 int vrc = RTThreadCreate(NULL, Appliance::taskThreadWriteOVF, this,
410 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
411 "Appliance::Task");
412 ComAssertMsgRCRet(vrc,
413 ("Could not create taskThreadExportOVF (%Rrc)\n", vrc), E_FAIL);
414
415 return S_OK;
416}
417
418////////////////////////////////////////////////////////////////////////////////
419//
420// Appliance constructor / destructor
421//
422////////////////////////////////////////////////////////////////////////////////
423
424DEFINE_EMPTY_CTOR_DTOR(Appliance)
425struct shutup {};
426
427/**
428 * Appliance COM initializer.
429 * @param
430 * @return
431 */
432
433HRESULT Appliance::init(VirtualBox *aVirtualBox)
434{
435 /* Enclose the state transition NotReady->InInit->Ready */
436 AutoInitSpan autoInitSpan(this);
437 AssertReturn(autoInitSpan.isOk(), E_FAIL);
438
439 /* Weak reference to a VirtualBox object */
440 unconst(mVirtualBox) = aVirtualBox;
441
442 // initialize data
443 m = new Data;
444
445 /* Confirm a successful initialization */
446 autoInitSpan.setSucceeded();
447
448 return S_OK;
449}
450
451/**
452 * Appliance COM uninitializer.
453 * @return
454 */
455void Appliance::uninit()
456{
457 delete m;
458 m = NULL;
459}
460
461////////////////////////////////////////////////////////////////////////////////
462//
463// Appliance private methods
464//
465////////////////////////////////////////////////////////////////////////////////
466
467/**
468 * Private helper method that goes thru the elements of the given "current" element in the OVF XML
469 * and handles the contained child elements (which can be "Section" or "Content" elements).
470 *
471 * @param pcszPath Path spec of the XML file, for error messages.
472 * @param pReferencesElement "References" element from OVF, for looking up file specifications; can be NULL if no such element is present.
473 * @param pCurElem Element whose children are to be analyzed here.
474 * @return
475 */
476HRESULT Appliance::LoopThruSections(const char *pcszPath,
477 const xml::ElementNode *pReferencesElem,
478 const xml::ElementNode *pCurElem)
479{
480 HRESULT rc;
481
482 xml::NodesLoop loopChildren(*pCurElem);
483 const xml::ElementNode *pElem;
484 while ((pElem = loopChildren.forAllNodes()))
485 {
486 const char *pcszElemName = pElem->getName();
487 const char *pcszTypeAttr = "";
488 const xml::AttributeNode *pTypeAttr;
489 if ((pTypeAttr = pElem->findAttribute("type")))
490 pcszTypeAttr = pTypeAttr->getValue();
491
492 if ( (!strcmp(pcszElemName, "DiskSection"))
493 || ( (!strcmp(pcszElemName, "Section"))
494 && (!strcmp(pcszTypeAttr, "ovf:DiskSection_Type"))
495 )
496 )
497 {
498 if (!(SUCCEEDED((rc = HandleDiskSection(pcszPath, pReferencesElem, pElem)))))
499 return rc;
500 }
501 else if ( (!strcmp(pcszElemName, "NetworkSection")) // we ignore NetworkSections for now
502 || ( (!strcmp(pcszElemName, "Section"))
503 && (!strcmp(pcszTypeAttr, "ovf:NetworkSection_Type"))
504 )
505 )
506 {
507 if (!(SUCCEEDED((rc = HandleNetworkSection(pcszPath, pElem)))))
508 return rc;
509 }
510 else if ( (!strcmp(pcszElemName, "DeploymentOptionSection>")))
511 {
512 // TODO
513 }
514 else if ( (!strcmp(pcszElemName, "Info")))
515 {
516 // child of VirtualSystemCollection -- TODO
517 }
518 else if ( (!strcmp(pcszElemName, "ResourceAllocationSection")))
519 {
520 // child of VirtualSystemCollection -- TODO
521 }
522 else if ( (!strcmp(pcszElemName, "StartupSection")))
523 {
524 // child of VirtualSystemCollection -- TODO
525 }
526 else if ( (!strcmp(pcszElemName, "VirtualSystem"))
527 || ( (!strcmp(pcszElemName, "Content"))
528 && (!strcmp(pcszTypeAttr, "ovf:VirtualSystem_Type"))
529 )
530 )
531 {
532 if (!(SUCCEEDED((rc = HandleVirtualSystemContent(pcszPath, pElem)))))
533 return rc;
534 }
535 else if ( (!strcmp(pcszElemName, "VirtualSystemCollection"))
536 || ( (!strcmp(pcszElemName, "Content"))
537 && (!strcmp(pcszTypeAttr, "ovf:VirtualSystemCollection_Type"))
538 )
539 )
540 {
541 // TODO ResourceAllocationSection
542
543 // recurse for this, since it has VirtualSystem elements as children
544 if (!(SUCCEEDED((rc = LoopThruSections(pcszPath, pReferencesElem, pElem)))))
545 return rc;
546 }
547 }
548
549 return S_OK;
550}
551
552/**
553 * Private helper method that handles disk sections in the OVF XML.
554 * Gets called indirectly from IAppliance::read().
555 *
556 * @param pcszPath Path spec of the XML file, for error messages.
557 * @param pReferencesElement "References" element from OVF, for looking up file specifications; can be NULL if no such element is present.
558 * @param pSectionElem Section element for which this helper is getting called.
559 * @return
560 */
561HRESULT Appliance::HandleDiskSection(const char *pcszPath,
562 const xml::ElementNode *pReferencesElem,
563 const xml::ElementNode *pSectionElem)
564{
565 // contains "Disk" child elements
566 xml::NodesLoop loopDisks(*pSectionElem, "Disk");
567 const xml::ElementNode *pelmDisk;
568 while ((pelmDisk = loopDisks.forAllNodes()))
569 {
570 DiskImage d;
571 const char *pcszBad = NULL;
572 if (!(pelmDisk->getAttributeValue("diskId", d.strDiskId)))
573 pcszBad = "diskId";
574 else if (!(pelmDisk->getAttributeValue("format", d.strFormat)))
575 pcszBad = "format";
576 else if (!(pelmDisk->getAttributeValue("capacity", d.iCapacity)))
577 pcszBad = "capacity";
578 else
579 {
580 if (!(pelmDisk->getAttributeValue("populatedSize", d.iPopulatedSize)))
581 // optional
582 d.iPopulatedSize = -1;
583
584 Utf8Str strFileRef;
585 if (pelmDisk->getAttributeValue("fileRef", strFileRef)) // optional
586 {
587 // look up corresponding /References/File nodes (list built above)
588 const xml::ElementNode *pFileElem;
589 if ( pReferencesElem
590 && ((pFileElem = pReferencesElem->findChildElementFromId(strFileRef.c_str())))
591 )
592 {
593 // copy remaining values from file node then
594 const char *pcszBadInFile = NULL;
595 if (!(pFileElem->getAttributeValue("href", d.strHref)))
596 pcszBadInFile = "href";
597 else if (!(pFileElem->getAttributeValue("size", d.iSize)))
598 d.iSize = -1; // optional
599 // if (!(pFileElem->getAttributeValue("size", d.iChunkSize))) TODO
600 d.iChunkSize = -1; // optional
601 pFileElem->getAttributeValue("compression", d.strCompression);
602
603 if (pcszBadInFile)
604 return setError(VBOX_E_FILE_ERROR,
605 tr("Error reading \"%s\": missing or invalid attribute '%s' in 'File' element, line %d"),
606 pcszPath,
607 pcszBadInFile,
608 pFileElem->getLineNumber());
609 }
610 else
611 return setError(VBOX_E_FILE_ERROR,
612 tr("Error reading \"%s\": cannot find References/File element for ID '%s' referenced by 'Disk' element, line %d"),
613 pcszPath,
614 strFileRef.c_str(),
615 pelmDisk->getLineNumber());
616 }
617 }
618
619 if (pcszBad)
620 return setError(VBOX_E_FILE_ERROR,
621 tr("Error reading \"%s\": missing or invalid attribute '%s' in 'DiskSection' element, line %d"),
622 pcszPath,
623 pcszBad,
624 pelmDisk->getLineNumber());
625
626 m->mapDisks[d.strDiskId] = d;
627 }
628
629 return S_OK;
630}
631
632/**
633 * Private helper method that handles network sections in the OVF XML.
634 * Gets called indirectly from IAppliance::read().
635 *
636 * @param pcszPath Path spec of the XML file, for error messages.
637 * @param pSectionElem Section element for which this helper is getting called.
638 * @return
639 */
640HRESULT Appliance::HandleNetworkSection(const char * /* pcszPath */,
641 const xml::ElementNode * /* pSectionElem */)
642{
643 // we ignore network sections for now
644
645// xml::NodesLoop loopNetworks(*pSectionElem, "Network");
646// const xml::Node *pelmNetwork;
647// while ((pelmNetwork = loopNetworks.forAllNodes()))
648// {
649// Network n;
650// if (!(pelmNetwork->getAttributeValue("name", n.strNetworkName)))
651// return setError(VBOX_E_FILE_ERROR,
652// tr("Error reading \"%s\": missing 'name' attribute in 'Network', line %d"),
653// pcszPath,
654// pelmNetwork->getLineNumber());
655//
656// m->mapNetworks[n.strNetworkName] = n;
657// }
658
659 return S_OK;
660}
661
662/**
663 * Private helper method that handles a "VirtualSystem" element in the OVF XML.
664 * Gets called indirectly from IAppliance::read().
665 *
666 * @param pcszPath
667 * @param pContentElem
668 * @return
669 */
670HRESULT Appliance::HandleVirtualSystemContent(const char *pcszPath,
671 const xml::ElementNode *pelmVirtualSystem)
672{
673 VirtualSystem vsys;
674
675 const xml::AttributeNode *pIdAttr = pelmVirtualSystem->findAttribute("id");
676 if (pIdAttr)
677 vsys.strName = pIdAttr->getValue();
678
679 xml::NodesLoop loop(*pelmVirtualSystem); // all child elements
680 const xml::ElementNode *pelmThis;
681 while ((pelmThis = loop.forAllNodes()))
682 {
683 const char *pcszElemName = pelmThis->getName();
684 const xml::AttributeNode *pTypeAttr = pelmThis->findAttribute("type");
685 const char *pcszTypeAttr = (pTypeAttr) ? pTypeAttr->getValue() : "";
686
687 if (!strcmp(pcszElemName, "EulaSection"))
688 {
689 /* <EulaSection>
690 <Info ovf:msgid="6">License agreement for the Virtual System.</Info>
691 <License ovf:msgid="1">License terms can go in here.</License>
692 </EulaSection> */
693
694 const xml::ElementNode *pelmInfo, *pelmLicense;
695 if ( ((pelmInfo = pelmThis->findChildElement("Info")))
696 && ((pelmLicense = pelmThis->findChildElement("License")))
697 )
698 {
699 vsys.strLicenceInfo = pelmInfo->getValue();
700 vsys.strLicenceText = pelmLicense->getValue();
701 }
702 }
703 else if ( (!strcmp(pcszElemName, "VirtualHardwareSection"))
704 || (!strcmp(pcszTypeAttr, "ovf:VirtualHardwareSection_Type"))
705 )
706 {
707 const xml::ElementNode *pelmSystem, *pelmVirtualSystemType;
708 if ((pelmSystem = pelmThis->findChildElement("System")))
709 {
710 /* <System>
711 <vssd:Description>Description of the virtual hardware section.</vssd:Description>
712 <vssd:ElementName>vmware</vssd:ElementName>
713 <vssd:InstanceID>1</vssd:InstanceID>
714 <vssd:VirtualSystemIdentifier>MyLampService</vssd:VirtualSystemIdentifier>
715 <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType>
716 </System>*/
717 if ((pelmVirtualSystemType = pelmSystem->findChildElement("VirtualSystemType")))
718 vsys.strVirtualSystemType = pelmVirtualSystemType->getValue();
719 }
720
721 xml::NodesLoop loopVirtualHardwareItems(*pelmThis, "Item"); // all "Item" child elements
722 const xml::ElementNode *pelmItem;
723 while ((pelmItem = loopVirtualHardwareItems.forAllNodes()))
724 {
725 VirtualHardwareItem i;
726
727 i.ulLineNumber = pelmItem->getLineNumber();
728
729 xml::NodesLoop loopItemChildren(*pelmItem); // all child elements
730 const xml::ElementNode *pelmItemChild;
731 while ((pelmItemChild = loopItemChildren.forAllNodes()))
732 {
733 const char *pcszItemChildName = pelmItemChild->getName();
734 if (!strcmp(pcszItemChildName, "Description"))
735 i.strDescription = pelmItemChild->getValue();
736 else if (!strcmp(pcszItemChildName, "Caption"))
737 i.strCaption = pelmItemChild->getValue();
738 else if (!strcmp(pcszItemChildName, "ElementName"))
739 i.strElementName = pelmItemChild->getValue();
740 else if ( (!strcmp(pcszItemChildName, "InstanceID"))
741 || (!strcmp(pcszItemChildName, "InstanceId"))
742 )
743 pelmItemChild->copyValue(i.ulInstanceID);
744 else if (!strcmp(pcszItemChildName, "HostResource"))
745 i.strHostResource = pelmItemChild->getValue();
746 else if (!strcmp(pcszItemChildName, "ResourceType"))
747 {
748 uint32_t ulType;
749 pelmItemChild->copyValue(ulType);
750 i.resourceType = (OVFResourceType_T)ulType;
751 }
752 else if (!strcmp(pcszItemChildName, "OtherResourceType"))
753 i.strOtherResourceType = pelmItemChild->getValue();
754 else if (!strcmp(pcszItemChildName, "ResourceSubType"))
755 i.strResourceSubType = pelmItemChild->getValue();
756 else if (!strcmp(pcszItemChildName, "AutomaticAllocation"))
757 i.fAutomaticAllocation = (!strcmp(pelmItemChild->getValue(), "true")) ? true : false;
758 else if (!strcmp(pcszItemChildName, "AutomaticDeallocation"))
759 i.fAutomaticDeallocation = (!strcmp(pelmItemChild->getValue(), "true")) ? true : false;
760 else if (!strcmp(pcszItemChildName, "Parent"))
761 pelmItemChild->copyValue(i.ulParent);
762 else if (!strcmp(pcszItemChildName, "Connection"))
763 i.strConnection = pelmItemChild->getValue();
764 else if (!strcmp(pcszItemChildName, "Address"))
765 i.strAddress = pelmItemChild->getValue();
766 else if (!strcmp(pcszItemChildName, "AddressOnParent"))
767 i.strAddressOnParent = pelmItemChild->getValue();
768 else if (!strcmp(pcszItemChildName, "AllocationUnits"))
769 i.strAllocationUnits = pelmItemChild->getValue();
770 else if (!strcmp(pcszItemChildName, "VirtualQuantity"))
771 pelmItemChild->copyValue(i.ullVirtualQuantity);
772 else if (!strcmp(pcszItemChildName, "Reservation"))
773 pelmItemChild->copyValue(i.ullReservation);
774 else if (!strcmp(pcszItemChildName, "Limit"))
775 pelmItemChild->copyValue(i.ullLimit);
776 else if (!strcmp(pcszItemChildName, "Weight"))
777 pelmItemChild->copyValue(i.ullWeight);
778 else if (!strcmp(pcszItemChildName, "ConsumerVisibility"))
779 i.strConsumerVisibility = pelmItemChild->getValue();
780 else if (!strcmp(pcszItemChildName, "MappingBehavior"))
781 i.strMappingBehavior = pelmItemChild->getValue();
782 else if (!strcmp(pcszItemChildName, "PoolID"))
783 i.strPoolID = pelmItemChild->getValue();
784 else if (!strcmp(pcszItemChildName, "BusNumber"))
785 pelmItemChild->copyValue(i.ulBusNumber);
786 else
787 return setError(VBOX_E_FILE_ERROR,
788 tr("Error reading \"%s\": unknown element \"%s\" under Item element, line %d"),
789 pcszPath,
790 pcszItemChildName,
791 i.ulLineNumber);
792 }
793
794 // store!
795 vsys.mapHardwareItems[i.ulInstanceID] = i;
796 }
797
798 HardwareItemsMap::const_iterator itH;
799
800 for (itH = vsys.mapHardwareItems.begin();
801 itH != vsys.mapHardwareItems.end();
802 ++itH)
803 {
804 const VirtualHardwareItem &i = itH->second;
805
806 // do some analysis
807 switch (i.resourceType)
808 {
809 case OVFResourceType_Processor: // 3
810 /* <rasd:Caption>1 virtual CPU</rasd:Caption>
811 <rasd:Description>Number of virtual CPUs</rasd:Description>
812 <rasd:ElementName>virtual CPU</rasd:ElementName>
813 <rasd:InstanceID>1</rasd:InstanceID>
814 <rasd:ResourceType>3</rasd:ResourceType>
815 <rasd:VirtualQuantity>1</rasd:VirtualQuantity>*/
816 if (i.ullVirtualQuantity < UINT16_MAX)
817 vsys.cCPUs = (uint16_t)i.ullVirtualQuantity;
818 else
819 return setError(VBOX_E_FILE_ERROR,
820 tr("Error reading \"%s\": CPU count %RI64 is larger than %d, line %d"),
821 pcszPath,
822 i.ullVirtualQuantity,
823 UINT16_MAX,
824 i.ulLineNumber);
825 break;
826
827 case OVFResourceType_Memory: // 4
828 if ( (i.strAllocationUnits == "MegaBytes") // found in OVF created by OVF toolkit
829 || (i.strAllocationUnits == "MB") // found in MS docs
830 || (i.strAllocationUnits == "byte * 2^20") // suggested by OVF spec DSP0243 page 21
831 )
832 vsys.ullMemorySize = i.ullVirtualQuantity * 1024 * 1024;
833 else
834 return setError(VBOX_E_FILE_ERROR,
835 tr("Error reading \"%s\": Invalid allocation unit \"%s\" specified with memory size item, line %d"),
836 pcszPath,
837 i.strAllocationUnits.c_str(),
838 i.ulLineNumber);
839 break;
840
841 case OVFResourceType_IdeController: // 5 IdeController
842 {
843 /* <Item>
844 <rasd:Caption>ideController0</rasd:Caption>
845 <rasd:Description>IDE Controller</rasd:Description>
846 <rasd:InstanceId>5</rasd:InstanceId>
847 <rasd:ResourceType>5</rasd:ResourceType>
848 <rasd:Address>0</rasd:Address>
849 <rasd:BusNumber>0</rasd:BusNumber>
850 </Item> */
851 HardDiskController hdc;
852 hdc.system = HardDiskController::IDE;
853 hdc.idController = i.ulInstanceID;
854 hdc.strAddress = i.strAddress;
855 hdc.ulBusNumber = i.ulBusNumber;
856
857 vsys.mapControllers[i.ulInstanceID] = hdc;
858 }
859 break;
860
861 case OVFResourceType_ParallelScsiHba: // 6 SCSI controller
862 {
863 /* <Item>
864 <rasd:Caption>SCSI Controller 0 - LSI Logic</rasd:Caption>
865 <rasd:Description>SCI Controller</rasd:Description>
866 <rasd:ElementName>SCSI controller</rasd:ElementName>
867 <rasd:InstanceID>4</rasd:InstanceID>
868 <rasd:ResourceSubType>LsiLogic</rasd:ResourceSubType>
869 <rasd:ResourceType>6</rasd:ResourceType>
870 </Item> */
871 HardDiskController hdc;
872 hdc.system = HardDiskController::SCSI;
873 hdc.idController = i.ulInstanceID;
874 hdc.strControllerType = i.strResourceSubType;
875
876 vsys.mapControllers[i.ulInstanceID] = hdc;
877 }
878 break;
879
880 case OVFResourceType_EthernetAdapter: // 10
881 {
882 /* <Item>
883 <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
884 <rasd:Caption>Ethernet adapter on 'VM Network'</rasd:Caption>
885 <rasd:Connection>VM Network</rasd:Connection>
886 <rasd:Description>VM Network?</rasd:Description>
887 <rasd:ElementName>Ethernet adapter</rasd:ElementName>
888 <rasd:InstanceID>3</rasd:InstanceID>
889 <rasd:ResourceType>10</rasd:ResourceType>
890 </Item>
891
892 OVF spec DSP 0243 page 21:
893 "For an Ethernet adapter, this specifies the abstract network connection name
894 for the virtual machine. All Ethernet adapters that specify the same abstract
895 network connection name within an OVF package shall be deployed on the same
896 network. The abstract network connection name shall be listed in the NetworkSection
897 at the outermost envelope level." */
898
899 // only store the name
900 vsys.llNetworkNames.push_back(i.strConnection);
901 }
902 break;
903
904 case OVFResourceType_FloppyDrive: // 14
905 vsys.fHasFloppyDrive = true; // we have no additional information
906 break;
907
908 case OVFResourceType_CdDrive: // 15
909 /* <Item ovf:required="false">
910 <rasd:Caption>cdrom1</rasd:Caption>
911 <rasd:InstanceId>7</rasd:InstanceId>
912 <rasd:ResourceType>15</rasd:ResourceType>
913 <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
914 <rasd:Parent>5</rasd:Parent>
915 <rasd:AddressOnParent>0</rasd:AddressOnParent>
916 </Item> */
917 // I tried to see what happens if I set an ISO for the CD-ROM in VMware Workstation,
918 // but then the ovftool dies with "Device backing not supported". So I guess if
919 // VMware can't export ISOs, then we don't need to be able to import them right now.
920 vsys.fHasCdromDrive = true; // we have no additional information
921 break;
922
923 case OVFResourceType_HardDisk: // 17
924 {
925 /* <Item>
926 <rasd:Caption>Harddisk 1</rasd:Caption>
927 <rasd:Description>HD</rasd:Description>
928 <rasd:ElementName>Hard Disk</rasd:ElementName>
929 <rasd:HostResource>ovf://disk/lamp</rasd:HostResource>
930 <rasd:InstanceID>5</rasd:InstanceID>
931 <rasd:Parent>4</rasd:Parent>
932 <rasd:ResourceType>17</rasd:ResourceType>
933 </Item> */
934
935 // look up the hard disk controller element whose InstanceID equals our Parent;
936 // this is how the connection is specified in OVF
937 ControllersMap::const_iterator it = vsys.mapControllers.find(i.ulParent);
938 if (it == vsys.mapControllers.end())
939 return setError(VBOX_E_FILE_ERROR,
940 tr("Error reading \"%s\": Hard disk item with instance ID %d specifies invalid parent %d, line %d"),
941 pcszPath,
942 i.ulInstanceID,
943 i.ulParent,
944 i.ulLineNumber);
945 //const HardDiskController &hdc = it->second;
946
947 VirtualDisk vd;
948 vd.idController = i.ulParent;
949 i.strAddressOnParent.toInt(vd.ulAddressOnParent);
950 // ovf://disk/lamp
951 // 123456789012345
952 if (i.strHostResource.substr(0, 11) == "ovf://disk/")
953 vd.strDiskId = i.strHostResource.substr(11);
954 else if (i.strHostResource.substr(0, 6) == "/disk/")
955 vd.strDiskId = i.strHostResource.substr(6);
956
957 if ( !(vd.strDiskId.length())
958 || (m->mapDisks.find(vd.strDiskId) == m->mapDisks.end())
959 )
960 return setError(VBOX_E_FILE_ERROR,
961 tr("Error reading \"%s\": Hard disk item with instance ID %d specifies invalid host resource \"%s\", line %d"),
962 pcszPath,
963 i.ulInstanceID,
964 i.strHostResource.c_str(),
965 i.ulLineNumber);
966
967 vsys.mapVirtualDisks[vd.strDiskId] = vd;
968 }
969 break;
970
971 case OVFResourceType_OtherStorageDevice: // 20 SATA controller
972 {
973 /* <Item>
974 <rasd:Description>SATA Controller</rasd:Description>
975 <rasd:Caption>sataController0</rasd:Caption>
976 <rasd:InstanceID>4</rasd:InstanceID>
977 <rasd:ResourceType>20</rasd:ResourceType>
978 <rasd:ResourceSubType>AHCI</rasd:ResourceSubType>
979 <rasd:Address>0</rasd:Address>
980 <rasd:BusNumber>0</rasd:BusNumber>
981 </Item> */
982 if (i.strCaption.startsWith ("sataController", Utf8Str::CaseInsensitive) &&
983 !i.strResourceSubType.compare ("AHCI", Utf8Str::CaseInsensitive))
984 {
985 HardDiskController hdc;
986 hdc.system = HardDiskController::SATA;
987 hdc.idController = i.ulInstanceID;
988 hdc.strControllerType = i.strResourceSubType;
989
990 vsys.mapControllers[i.ulInstanceID] = hdc;
991 }
992 else
993 return setError(VBOX_E_FILE_ERROR,
994 tr("Error reading \"%s\": Host resource of type \"Other Storage Device (%d)\" is supported with SATA AHCI controllers only, line %d"),
995 pcszPath,
996 OVFResourceType_OtherStorageDevice,
997 i.ulLineNumber);
998 }
999 break;
1000
1001 case OVFResourceType_UsbController: // 23
1002 /* <Item ovf:required="false">
1003 <rasd:Caption>usb</rasd:Caption>
1004 <rasd:Description>USB Controller</rasd:Description>
1005 <rasd:InstanceId>3</rasd:InstanceId>
1006 <rasd:ResourceType>23</rasd:ResourceType>
1007 <rasd:Address>0</rasd:Address>
1008 <rasd:BusNumber>0</rasd:BusNumber>
1009 </Item> */
1010 vsys.fHasUsbController = true; // we have no additional information
1011 break;
1012
1013 case OVFResourceType_SoundCard: // 35
1014 /* <Item ovf:required="false">
1015 <rasd:Caption>sound</rasd:Caption>
1016 <rasd:Description>Sound Card</rasd:Description>
1017 <rasd:InstanceId>10</rasd:InstanceId>
1018 <rasd:ResourceType>35</rasd:ResourceType>
1019 <rasd:ResourceSubType>ensoniq1371</rasd:ResourceSubType>
1020 <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
1021 <rasd:AddressOnParent>3</rasd:AddressOnParent>
1022 </Item> */
1023 vsys.strSoundCardType = i.strResourceSubType;
1024 break;
1025
1026 default:
1027 return setError(VBOX_E_FILE_ERROR,
1028 tr("Error reading \"%s\": Unknown resource type %d in hardware item, line %d"),
1029 pcszPath,
1030 i.resourceType,
1031 i.ulLineNumber);
1032 }
1033 }
1034 }
1035 else if ( (!strcmp(pcszElemName, "OperatingSystemSection"))
1036 || (!strcmp(pcszTypeAttr, "ovf:OperatingSystemSection_Type"))
1037 )
1038 {
1039 uint64_t cimos64;
1040 if (!(pelmThis->getAttributeValue("id", cimos64)))
1041 return setError(VBOX_E_FILE_ERROR,
1042 tr("Error reading \"%s\": missing or invalid 'ovf:id' attribute in operating system section element, line %d"),
1043 pcszPath,
1044 pelmThis->getLineNumber());
1045
1046 vsys.cimos = (CIMOSType_T)cimos64;
1047 }
1048 }
1049
1050 // now create the virtual system
1051 m->llVirtualSystems.push_back(vsys);
1052
1053 return S_OK;
1054}
1055
1056////////////////////////////////////////////////////////////////////////////////
1057//
1058// IAppliance public methods
1059//
1060////////////////////////////////////////////////////////////////////////////////
1061
1062/**
1063 * Public method implementation.
1064 * @param
1065 * @return
1066 */
1067STDMETHODIMP Appliance::COMGETTER(Path)(BSTR *aPath)
1068{
1069 if (!aPath)
1070 return E_POINTER;
1071
1072 AutoCaller autoCaller(this);
1073 CheckComRCReturnRC(autoCaller.rc());
1074
1075 AutoReadLock alock(this);
1076
1077 Bstr bstrPath(m->strPath);
1078 bstrPath.cloneTo(aPath);
1079
1080 return S_OK;
1081}
1082
1083/**
1084 * Public method implementation.
1085 * @param
1086 * @return
1087 */
1088STDMETHODIMP Appliance::COMGETTER(Disks)(ComSafeArrayOut(BSTR, aDisks))
1089{
1090 CheckComArgOutSafeArrayPointerValid(aDisks);
1091
1092 AutoCaller autoCaller(this);
1093 CheckComRCReturnRC(autoCaller.rc());
1094
1095 AutoReadLock alock(this);
1096
1097 size_t c = m->mapDisks.size();
1098 com::SafeArray<BSTR> sfaDisks(c);
1099
1100 DiskImagesMap::const_iterator it;
1101 size_t i = 0;
1102 for (it = m->mapDisks.begin();
1103 it != m->mapDisks.end();
1104 ++it, ++i)
1105 {
1106 // create a string representing this disk
1107 const DiskImage &d = it->second;
1108 char *psz = NULL;
1109 RTStrAPrintf(&psz,
1110 "%s\t"
1111 "%RI64\t"
1112 "%RI64\t"
1113 "%s\t"
1114 "%s\t"
1115 "%RI64\t"
1116 "%RI64\t"
1117 "%s",
1118 d.strDiskId.c_str(),
1119 d.iCapacity,
1120 d.iPopulatedSize,
1121 d.strFormat.c_str(),
1122 d.strHref.c_str(),
1123 d.iSize,
1124 d.iChunkSize,
1125 d.strCompression.c_str());
1126 Utf8Str utf(psz);
1127 Bstr bstr(utf);
1128 // push to safearray
1129 bstr.cloneTo(&sfaDisks[i]);
1130 RTStrFree(psz);
1131 }
1132
1133 sfaDisks.detachTo(ComSafeArrayOutArg(aDisks));
1134
1135 return S_OK;
1136}
1137
1138/**
1139 * Public method implementation.
1140 * @param
1141 * @return
1142 */
1143STDMETHODIMP Appliance::COMGETTER(VirtualSystemDescriptions)(ComSafeArrayOut(IVirtualSystemDescription*, aVirtualSystemDescriptions))
1144{
1145 CheckComArgOutSafeArrayPointerValid(aVirtualSystemDescriptions);
1146
1147 AutoCaller autoCaller(this);
1148 CheckComRCReturnRC(autoCaller.rc());
1149
1150 AutoReadLock alock(this);
1151
1152 SafeIfaceArray<IVirtualSystemDescription> sfaVSD(m->virtualSystemDescriptions);
1153 sfaVSD.detachTo(ComSafeArrayOutArg(aVirtualSystemDescriptions));
1154
1155 return S_OK;
1156}
1157
1158/**
1159 * Public method implementation.
1160 * @param path
1161 * @return
1162 */
1163STDMETHODIMP Appliance::Read(IN_BSTR path)
1164{
1165 HRESULT rc = S_OK;
1166
1167 if (!path)
1168 return E_POINTER;
1169
1170 AutoCaller autoCaller(this);
1171 CheckComRCReturnRC(autoCaller.rc());
1172
1173 AutoWriteLock alock(this);
1174
1175 // see if we can handle this file; for now we insist it has an ".ovf" extension
1176 m->strPath = path;
1177 if (!m->strPath.endsWith(".ovf", Utf8Str::CaseInsensitive))
1178 return setError(VBOX_E_FILE_ERROR,
1179 tr("Appliance file must have .ovf extension"));
1180
1181 try
1182 {
1183 xml::XmlFileParser parser;
1184 xml::Document doc;
1185 parser.read(m->strPath.raw(),
1186 doc);
1187
1188 const xml::ElementNode *pRootElem = doc.getRootElement();
1189 if (strcmp(pRootElem->getName(), "Envelope"))
1190 return setError(VBOX_E_FILE_ERROR,
1191 tr("Root element in OVF file must be \"Envelope\"."));
1192
1193 // OVF has the following rough layout:
1194 /*
1195 -- <References> .... files referenced from other parts of the file, such as VMDK images
1196 -- Metadata, comprised of several section commands
1197 -- virtual machines, either a single <VirtualSystem>, or a <VirtualSystemCollection>
1198 -- optionally <Strings> for localization
1199 */
1200
1201 // get all "File" child elements of "References" section so we can look up files easily;
1202 // first find the "References" sections so we can look up files
1203 xml::ElementNodesList listFileElements; // receives all /Envelope/References/File nodes
1204 const xml::ElementNode *pReferencesElem;
1205 if ((pReferencesElem = pRootElem->findChildElement("References")))
1206 pReferencesElem->getChildElements(listFileElements, "File");
1207
1208 // now go though the sections
1209 if (!(SUCCEEDED(rc = LoopThruSections(m->strPath.raw(), pReferencesElem, pRootElem))))
1210 return rc;
1211 }
1212 catch(xml::Error &x)
1213 {
1214 return setError(VBOX_E_FILE_ERROR,
1215 x.what());
1216 }
1217
1218 return S_OK;
1219}
1220
1221/**
1222 * Public method implementation.
1223 * @return
1224 */
1225STDMETHODIMP Appliance::Interpret()
1226{
1227 // @todo:
1228 // - don't use COM methods but the methods directly (faster, but needs appropriate locking of that objects itself (s. HardDisk))
1229 // - Appropriate handle errors like not supported file formats
1230 AutoCaller autoCaller(this);
1231 CheckComRCReturnRC(autoCaller.rc());
1232
1233 AutoWriteLock(this);
1234
1235 HRESULT rc = S_OK;
1236
1237 /* Clear any previous virtual system descriptions */
1238 m->virtualSystemDescriptions.clear();
1239
1240 /* We need the default path for storing disk images */
1241 ComPtr<ISystemProperties> systemProps;
1242 rc = mVirtualBox->COMGETTER(SystemProperties)(systemProps.asOutParam());
1243 CheckComRCReturnRC(rc);
1244 Bstr bstrDefaultHardDiskLocation;
1245 rc = systemProps->COMGETTER(DefaultHardDiskFolder)(bstrDefaultHardDiskLocation.asOutParam());
1246 CheckComRCReturnRC(rc);
1247
1248 /* Try/catch so we can clean up on error */
1249 try
1250 {
1251 list<VirtualSystem>::const_iterator it;
1252 /* Iterate through all virtual systems */
1253 for (it = m->llVirtualSystems.begin();
1254 it != m->llVirtualSystems.end();
1255 ++it)
1256 {
1257 const VirtualSystem &vsysThis = *it;
1258
1259 ComObjPtr<VirtualSystemDescription> pNewDesc;
1260 rc = pNewDesc.createObject();
1261 CheckComRCThrowRC(rc);
1262 rc = pNewDesc->init();
1263 CheckComRCThrowRC(rc);
1264
1265 /* Guest OS type */
1266 Utf8Str strOsTypeVBox,
1267 strCIMOSType = Utf8StrFmt("%RI32", (uint32_t)vsysThis.cimos);
1268 convertCIMOSType2VBoxOSType(strOsTypeVBox, vsysThis.cimos);
1269 pNewDesc->addEntry(VirtualSystemDescriptionType_OS,
1270 "",
1271 strCIMOSType,
1272 strOsTypeVBox);
1273
1274 /* VM name */
1275 /* If the there isn't any name specified create a default one out of
1276 * the OS type */
1277 Utf8Str nameVBox = vsysThis.strName;
1278 if (nameVBox.isEmpty())
1279 nameVBox = strOsTypeVBox;
1280 searchUniqueVMName(nameVBox);
1281 pNewDesc->addEntry(VirtualSystemDescriptionType_Name,
1282 "",
1283 vsysThis.strName,
1284 nameVBox);
1285
1286 /* Now that we know the OS type, get our internal defaults based on that. */
1287 ComPtr<IGuestOSType> pGuestOSType;
1288 rc = mVirtualBox->GetGuestOSType(Bstr(strOsTypeVBox), pGuestOSType.asOutParam());
1289 CheckComRCThrowRC(rc);
1290
1291 /* CPU count */
1292 ULONG cpuCountVBox = vsysThis.cCPUs;
1293 /* Check for the constrains */
1294 if (cpuCountVBox > 1) //SchemaDefs::MaxCPUCount)
1295 {
1296 addWarning(tr("The virtual system \"%s\" claims support for %u CPU's, but VirtualBox has support for max %u CPU's only."),
1297 vsysThis.strName.c_str(), cpuCountVBox, 1); //SchemaDefs::MaxCPUCount);
1298 cpuCountVBox = 1; //SchemaDefs::MaxCPUCount;
1299 }
1300 if (vsysThis.cCPUs == 0)
1301 cpuCountVBox = 1;
1302 pNewDesc->addEntry(VirtualSystemDescriptionType_CPU,
1303 "",
1304 Utf8StrFmt("%RI32", (uint32_t)vsysThis.cCPUs),
1305 Utf8StrFmt("%RI32", (uint32_t)cpuCountVBox));
1306
1307 /* RAM */
1308 uint64_t ullMemSizeVBox = vsysThis.ullMemorySize / _1M;
1309 /* Check for the constrains */
1310 if (ullMemSizeVBox != 0 &&
1311 (ullMemSizeVBox < static_cast<uint64_t>(SchemaDefs::MinGuestRAM) ||
1312 ullMemSizeVBox > static_cast<uint64_t>(SchemaDefs::MaxGuestRAM)))
1313 {
1314 addWarning(tr("The virtual system \"%s\" claims support for %llu MB RAM size, but VirtualBox has support for min %u & max %u MB RAM size only."),
1315 vsysThis.strName.c_str(), ullMemSizeVBox, SchemaDefs::MinGuestRAM, SchemaDefs::MaxGuestRAM);
1316 ullMemSizeVBox = RT_MIN(RT_MAX(ullMemSizeVBox, static_cast<uint64_t>(SchemaDefs::MinGuestRAM)), static_cast<uint64_t>(SchemaDefs::MaxGuestRAM));
1317 }
1318 if (vsysThis.ullMemorySize == 0)
1319 {
1320 /* If the RAM of the OVF is zero, use our predefined values */
1321 ULONG memSizeVBox2;
1322 rc = pGuestOSType->COMGETTER(RecommendedRAM)(&memSizeVBox2);
1323 CheckComRCThrowRC(rc);
1324 /* VBox stores that in MByte */
1325 ullMemSizeVBox = (uint64_t)memSizeVBox2;
1326 }
1327 pNewDesc->addEntry(VirtualSystemDescriptionType_Memory,
1328 "",
1329 Utf8StrFmt("%RI64", (uint64_t)vsysThis.ullMemorySize),
1330 Utf8StrFmt("%RI64", (uint64_t)ullMemSizeVBox));
1331
1332 /* Audio */
1333 if (!vsysThis.strSoundCardType.isNull())
1334 /* Currently we set the AC97 always.
1335 @todo: figure out the hardware which could be possible */
1336 pNewDesc->addEntry(VirtualSystemDescriptionType_SoundCard,
1337 "",
1338 vsysThis.strSoundCardType,
1339 Utf8StrFmt("%RI32", (uint32_t)AudioControllerType_AC97));
1340
1341#ifdef VBOX_WITH_USB
1342 /* USB Controller */
1343 if (vsysThis.fHasUsbController)
1344 pNewDesc->addEntry(VirtualSystemDescriptionType_USBController, "", "", "");
1345#endif /* VBOX_WITH_USB */
1346
1347 /* Network Controller */
1348 // @todo: there is no hardware specification in the OVF file; supposedly the
1349 // hardware will then be determined by the VirtualSystemType element (e.g. "vmx-07")
1350 if (vsysThis.llNetworkNames.size() > 0)
1351 {
1352 /* Check for the constrains */
1353 if (vsysThis.llNetworkNames.size() > SchemaDefs::NetworkAdapterCount)
1354 addWarning(tr("The virtual system \"%s\" claims support for %u network adapters, but VirtualBox has support for max %u network adapter only."),
1355 vsysThis.strName.c_str(), vsysThis.llNetworkNames.size(), SchemaDefs::NetworkAdapterCount);
1356
1357 /* Get the default network adapter type for the selected guest OS */
1358 NetworkAdapterType_T nwAdapterVBox = NetworkAdapterType_Am79C970A;
1359 rc = pGuestOSType->COMGETTER(AdapterType)(&nwAdapterVBox);
1360 CheckComRCThrowRC(rc);
1361 list<Utf8Str>::const_iterator nwIt;
1362 /* Iterate through all abstract networks. We support 8 network
1363 * adapters at the maximum, so the first 8 will be added only. */
1364 size_t a = 0;
1365 for (nwIt = vsysThis.llNetworkNames.begin();
1366 nwIt != vsysThis.llNetworkNames.end() && a < SchemaDefs::NetworkAdapterCount;
1367 ++nwIt, ++a)
1368 {
1369 Utf8Str strNetwork = *nwIt; // logical network to connect to
1370 // make sure it's one of these two
1371 if ( (strNetwork.compare("Null", Utf8Str::CaseInsensitive))
1372 && (strNetwork.compare("Bridged", Utf8Str::CaseInsensitive))
1373 && (strNetwork.compare("Internal", Utf8Str::CaseInsensitive))
1374 && (strNetwork.compare("HostOnly", Utf8Str::CaseInsensitive))
1375 )
1376 strNetwork = "NAT";
1377
1378 pNewDesc->addEntry(VirtualSystemDescriptionType_NetworkAdapter,
1379 "", // ref
1380 strNetwork, // orig
1381 Utf8StrFmt("%RI32", (uint32_t)nwAdapterVBox), // conf
1382 Utf8StrFmt("type=%s", strNetwork.c_str())); // extra conf
1383 }
1384 }
1385
1386 /* Floppy Drive */
1387 if (vsysThis.fHasFloppyDrive)
1388 pNewDesc->addEntry(VirtualSystemDescriptionType_Floppy, "", "", "");
1389
1390 /* CD Drive */
1391 /* @todo: I can't disable the CDROM. So nothing to do for now */
1392 /*
1393 if (vsysThis.fHasCdromDrive)
1394 pNewDesc->addEntry(VirtualSystemDescriptionType_CDROM, "", "", "");*/
1395
1396 /* Hard disk Controller */
1397 uint16_t cIDEused = 0;
1398 uint16_t cSATAused = 0;
1399 uint16_t cSCSIused = 0;
1400 ControllersMap::const_iterator hdcIt;
1401 /* Iterate through all hard disk controllers */
1402 for (hdcIt = vsysThis.mapControllers.begin();
1403 hdcIt != vsysThis.mapControllers.end();
1404 ++hdcIt)
1405 {
1406 const HardDiskController &hdc = hdcIt->second;
1407 Utf8Str strControllerID = Utf8StrFmt("%RI32", (uint32_t)hdc.idController);
1408
1409 switch (hdc.system)
1410 {
1411 case HardDiskController::IDE:
1412 {
1413 /* Check for the constrains */
1414 /* @todo: I'm very confused! Are these bits *one* controller or
1415 is every port/bus declared as an extra controller. */
1416 if (cIDEused < 4)
1417 {
1418 // @todo: figure out the IDE types
1419 /* Use PIIX4 as default */
1420 Utf8Str strType = "PIIX4";
1421 if (!hdc.strControllerType.compare("PIIX3", Utf8Str::CaseInsensitive))
1422 strType = "PIIX3";
1423 else if (!hdc.strControllerType.compare("ICH6", Utf8Str::CaseInsensitive))
1424 strType = "ICH6";
1425 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerIDE,
1426 strControllerID,
1427 hdc.strControllerType,
1428 strType);
1429 }
1430 else
1431 {
1432 /* Warn only once */
1433 if (cIDEused == 1)
1434 addWarning(tr("The virtual \"%s\" system requests support for more than one IDE controller, but VirtualBox has support for only one."),
1435 vsysThis.strName.c_str());
1436
1437 }
1438 ++cIDEused;
1439 break;
1440 }
1441
1442#ifdef VBOX_WITH_AHCI
1443 case HardDiskController::SATA:
1444 {
1445 /* Check for the constrains */
1446 if (cSATAused < 1)
1447 {
1448 // @todo: figure out the SATA types
1449 /* We only support a plain AHCI controller, so use them always */
1450 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSATA,
1451 strControllerID,
1452 hdc.strControllerType,
1453 "AHCI");
1454 }
1455 else
1456 {
1457 /* Warn only once */
1458 if (cSATAused == 1)
1459 addWarning(tr("The virtual system \"%s\" requests support for more than one SATA controller, but VirtualBox has support for only one"),
1460 vsysThis.strName.c_str());
1461
1462 }
1463 ++cSATAused;
1464 break;
1465 }
1466#endif /* VBOX_WITH_AHCI */
1467
1468 case HardDiskController::SCSI:
1469 {
1470 /* Check for the constrains */
1471 if (cSCSIused < 1)
1472 {
1473 Utf8Str hdcController = "LsiLogic";
1474 if (!hdc.strControllerType.compare("BusLogic", Utf8Str::CaseInsensitive))
1475 hdcController = "BusLogic";
1476 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSCSI,
1477 strControllerID,
1478 hdc.strControllerType,
1479 hdcController);
1480 }
1481 else
1482 addWarning(tr("The virtual system \"%s\" requests support for an additional SCSI controller of type \"%s\" with ID %s, but VirtualBox presently supports only one SCSI controller."),
1483 vsysThis.strName.c_str(),
1484 hdc.strControllerType.c_str(),
1485 strControllerID.c_str());
1486 ++cSCSIused;
1487 break;
1488 }
1489 }
1490 }
1491
1492 /* Hard disks */
1493 if (vsysThis.mapVirtualDisks.size() > 0)
1494 {
1495 VirtualDisksMap::const_iterator itVD;
1496 /* Iterate through all hard disks ()*/
1497 for (itVD = vsysThis.mapVirtualDisks.begin();
1498 itVD != vsysThis.mapVirtualDisks.end();
1499 ++itVD)
1500 {
1501 const VirtualDisk &hd = itVD->second;
1502 /* Get the associated disk image */
1503 const DiskImage &di = m->mapDisks[hd.strDiskId];
1504
1505 // @todo:
1506 // - figure out all possible vmdk formats we also support
1507 // - figure out if there is a url specifier for vhd already
1508 // - we need a url specifier for the vdi format
1509 if ( di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#sparse", Utf8Str::CaseInsensitive)
1510 || di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive))
1511 {
1512 /* If the href is empty use the VM name as filename */
1513 Utf8Str strFilename = di.strHref;
1514 if (!strFilename.length())
1515 strFilename = Utf8StrFmt("%s.vmdk", nameVBox.c_str());
1516 /* Construct a unique target path */
1517 Utf8StrFmt strPath("%ls%c%s",
1518 bstrDefaultHardDiskLocation.raw(),
1519 RTPATH_DELIMITER,
1520 strFilename.c_str());
1521 searchUniqueDiskImageFilePath(strPath);
1522
1523 /* find the description for the hard disk controller
1524 * that has the same ID as hd.idController */
1525 const VirtualSystemDescriptionEntry *pController;
1526 if (!(pController = pNewDesc->findControllerFromID(hd.idController)))
1527 throw setError(E_FAIL,
1528 tr("Cannot find hard disk controller with OVF instance ID %RI32 to which disk \"%s\" should be attached"),
1529 hd.idController,
1530 di.strHref.c_str());
1531
1532 /* controller to attach to, and the bus within that controller */
1533 Utf8StrFmt strExtraConfig("controller=%RI16;channel=%RI16",
1534 pController->ulIndex,
1535 hd.ulAddressOnParent);
1536 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskImage,
1537 hd.strDiskId,
1538 di.strHref,
1539 strPath,
1540 strExtraConfig);
1541 }
1542 else
1543 throw setError(VBOX_E_FILE_ERROR,
1544 tr("Unsupported format for virtual disk image in OVF: \"%s\"", di.strFormat.c_str()));
1545 }
1546 }
1547
1548 m->virtualSystemDescriptions.push_back(pNewDesc);
1549 }
1550 }
1551 catch (HRESULT aRC)
1552 {
1553 /* On error we clear the list & return */
1554 m->virtualSystemDescriptions.clear();
1555 rc = aRC;
1556 }
1557
1558 return rc;
1559}
1560
1561/**
1562 * Public method implementation.
1563 * @param aProgress
1564 * @return
1565 */
1566STDMETHODIMP Appliance::ImportMachines(IProgress **aProgress)
1567{
1568 CheckComArgOutPointerValid(aProgress);
1569
1570 AutoCaller autoCaller(this);
1571 CheckComRCReturnRC(autoCaller.rc());
1572
1573 AutoReadLock(this);
1574
1575 HRESULT rc = S_OK;
1576
1577 ComObjPtr<Progress> progress;
1578 try
1579 {
1580 uint32_t opCount = calcMaxProgress();
1581 Bstr progressDesc = BstrFmt(tr("Import appliance '%s'"),
1582 m->strPath.raw());
1583 /* Create the progress object */
1584 progress.createObject();
1585 rc = progress->init(mVirtualBox, static_cast<IAppliance*>(this),
1586 progressDesc,
1587 FALSE /* aCancelable */,
1588 opCount,
1589 progressDesc);
1590 if (FAILED(rc)) throw rc;
1591
1592 /* Initialize our worker task */
1593 std::auto_ptr<TaskImportMachines> task(new TaskImportMachines(this, progress));
1594 //AssertComRCThrowRC (task->autoCaller.rc());
1595
1596 rc = task->startThread();
1597 if (FAILED(rc)) throw rc;
1598
1599 task.release();
1600 }
1601 catch (HRESULT aRC)
1602 {
1603 rc = aRC;
1604 }
1605
1606 if (SUCCEEDED(rc))
1607 /* Return progress to the caller */
1608 progress.queryInterfaceTo(aProgress);
1609
1610 return rc;
1611}
1612
1613STDMETHODIMP Appliance::Write(IN_BSTR path, IProgress **aProgress)
1614{
1615 HRESULT rc = S_OK;
1616
1617 CheckComArgOutPointerValid(aProgress);
1618
1619 AutoCaller autoCaller(this);
1620 if (FAILED(rc = autoCaller.rc())) return rc;
1621
1622 AutoWriteLock(this);
1623
1624 // see if we can handle this file; for now we insist it has an ".ovf" extension
1625 m->strPath = path;
1626 if (!m->strPath.endsWith(".ovf", Utf8Str::CaseInsensitive))
1627 return setError(VBOX_E_FILE_ERROR,
1628 tr("Appliance file must have .ovf extension"));
1629
1630 ComObjPtr<Progress> progress;
1631 try
1632 {
1633 uint32_t opCount = calcMaxProgress();
1634 Bstr progressDesc = BstrFmt(tr("Write appliance '%s'"),
1635 m->strPath.raw());
1636 /* Create the progress object */
1637 progress.createObject();
1638 rc = progress->init(mVirtualBox, static_cast<IAppliance*>(this),
1639 progressDesc,
1640 FALSE /* aCancelable */,
1641 opCount,
1642 progressDesc);
1643 CheckComRCThrowRC(rc);
1644
1645 /* Initialize our worker task */
1646 std::auto_ptr<TaskWriteOVF> task(new TaskWriteOVF(this, progress));
1647 //AssertComRCThrowRC (task->autoCaller.rc());
1648
1649 rc = task->startThread();
1650 CheckComRCThrowRC(rc);
1651
1652 task.release();
1653 }
1654 catch (HRESULT aRC)
1655 {
1656 rc = aRC;
1657 }
1658
1659 if (SUCCEEDED(rc))
1660 /* Return progress to the caller */
1661 progress.queryInterfaceTo(aProgress);
1662
1663 return rc;
1664}
1665
1666/**
1667* Public method implementation.
1668 * @return
1669 */
1670STDMETHODIMP Appliance::GetWarnings(ComSafeArrayOut(BSTR, aWarnings))
1671{
1672 if (ComSafeArrayOutIsNull(aWarnings))
1673 return E_POINTER;
1674
1675 AutoCaller autoCaller(this);
1676 CheckComRCReturnRC(autoCaller.rc());
1677
1678 AutoReadLock alock(this);
1679
1680 com::SafeArray<BSTR> sfaWarnings(m->llWarnings.size());
1681
1682 list<Utf8Str>::const_iterator it;
1683 size_t i = 0;
1684 for (it = m->llWarnings.begin();
1685 it != m->llWarnings.end();
1686 ++it, ++i)
1687 {
1688 Bstr bstr = *it;
1689 bstr.cloneTo(&sfaWarnings[i]);
1690 }
1691
1692 sfaWarnings.detachTo(ComSafeArrayOutArg(aWarnings));
1693
1694 return S_OK;
1695}
1696
1697HRESULT Appliance::searchUniqueVMName(Utf8Str& aName) const
1698{
1699 IMachine *machine = NULL;
1700 char *tmpName = RTStrDup(aName.c_str());
1701 int i = 1;
1702 /* @todo: Maybe too cost-intensive; try to find a lighter way */
1703 while (mVirtualBox->FindMachine(Bstr(tmpName), &machine) != VBOX_E_OBJECT_NOT_FOUND)
1704 {
1705 RTStrFree(tmpName);
1706 RTStrAPrintf(&tmpName, "%s_%d", aName.c_str(), i);
1707 ++i;
1708 }
1709 aName = tmpName;
1710 RTStrFree(tmpName);
1711
1712 return S_OK;
1713}
1714
1715HRESULT Appliance::searchUniqueDiskImageFilePath(Utf8Str& aName) const
1716{
1717 IHardDisk *harddisk = NULL;
1718 char *tmpName = RTStrDup(aName.c_str());
1719 int i = 1;
1720 /* Check if the file exists or if a file with this path is registered
1721 * already */
1722 /* @todo: Maybe too cost-intensive; try to find a lighter way */
1723 while (RTPathExists(tmpName) ||
1724 mVirtualBox->FindHardDisk(Bstr(tmpName), &harddisk) != VBOX_E_OBJECT_NOT_FOUND)
1725 {
1726 RTStrFree(tmpName);
1727 char *tmpDir = RTStrDup(aName.c_str());
1728 RTPathStripFilename(tmpDir);;
1729 char *tmpFile = RTStrDup(RTPathFilename(aName.c_str()));
1730 RTPathStripExt(tmpFile);
1731 const char *tmpExt = RTPathExt(aName.c_str());
1732 RTStrAPrintf(&tmpName, "%s%c%s_%d%s", tmpDir, RTPATH_DELIMITER, tmpFile, i, tmpExt);
1733 RTStrFree(tmpFile);
1734 RTStrFree(tmpDir);
1735 ++i;
1736 }
1737 aName = tmpName;
1738 RTStrFree(tmpName);
1739
1740 return S_OK;
1741}
1742
1743/**
1744 * Calculates the maximum progress value for importMachines() and write().
1745 * @return
1746 */
1747uint32_t Appliance::calcMaxProgress()
1748{
1749 /* Figure out how many sub operation the import will need */
1750 /* One for the appliance */
1751 uint32_t opCount = 1;
1752 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;
1753 for (it = m->virtualSystemDescriptions.begin();
1754 it != m->virtualSystemDescriptions.end();
1755 ++it)
1756 {
1757 /* One for every Virtual System */
1758 ++opCount;
1759 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);
1760 /* One for every hard disk of the Virtual System */
1761 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
1762 opCount += (uint32_t)avsdeHDs.size();
1763 }
1764
1765 return opCount;
1766}
1767
1768void Appliance::addWarning(const char* aWarning, ...)
1769{
1770 va_list args;
1771 va_start(args, aWarning);
1772 Utf8StrFmtVA str(aWarning, args);
1773 va_end(args);
1774 m->llWarnings.push_back(str);
1775}
1776
1777struct MyHardDiskAttachment
1778{
1779 Guid uuid;
1780 ComPtr<IMachine> pMachine;
1781 Bstr controllerType;
1782 int32_t lChannel;
1783 int32_t lDevice;
1784};
1785
1786/**
1787 * Worker thread implementation for ImportMachines().
1788 * @param aThread
1789 * @param pvUser
1790 */
1791/* static */
1792DECLCALLBACK(int) Appliance::taskThreadImportMachines(RTTHREAD /* aThread */, void *pvUser)
1793{
1794 std::auto_ptr<TaskImportMachines> task(static_cast<TaskImportMachines*>(pvUser));
1795 AssertReturn(task.get(), VERR_GENERAL_FAILURE);
1796
1797 Appliance *pAppliance = task->pAppliance;
1798
1799 LogFlowFuncEnter();
1800 LogFlowFunc(("Appliance %p\n", pAppliance));
1801
1802 AutoCaller autoCaller(pAppliance);
1803 CheckComRCReturnRC(autoCaller.rc());
1804
1805 AutoWriteLock appLock(pAppliance);
1806
1807 HRESULT rc = S_OK;
1808
1809 ComPtr<IVirtualBox> pVirtualBox(pAppliance->mVirtualBox);
1810
1811 // rollback for errors:
1812 // 1) a list of images that we created/imported
1813 list<MyHardDiskAttachment> llHardDiskAttachments;
1814 list< ComPtr<IHardDisk> > llHardDisksCreated;
1815 list<Guid> llMachinesRegistered;
1816
1817 ComPtr<ISession> session;
1818 bool fSessionOpen = false;
1819 rc = session.createInprocObject(CLSID_Session);
1820 CheckComRCReturnRC(rc);
1821
1822 list<VirtualSystem>::const_iterator it;
1823 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it1;
1824 /* Iterate through all virtual systems of that appliance */
1825 size_t i = 0;
1826 for (it = pAppliance->m->llVirtualSystems.begin(),
1827 it1 = pAppliance->m->virtualSystemDescriptions.begin();
1828 it != pAppliance->m->llVirtualSystems.end();
1829 ++it, ++it1, ++i)
1830 {
1831 const VirtualSystem &vsysThis = *it;
1832 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it1);
1833
1834 ComPtr<IMachine> pNewMachine;
1835
1836 /* Catch possible errors */
1837 try
1838 {
1839 if (!task->progress.isNull())
1840 task->progress->advanceOperation(BstrFmt(tr("Importing Virtual System %d"), i + 1));
1841
1842 /* How many sub notifications are necessary? */
1843 const float opCountMax = 100.0/5;
1844 uint32_t opCount = 0;
1845
1846 /* Guest OS type */
1847 std::list<VirtualSystemDescriptionEntry*> vsdeOS;
1848 vsdeOS = vsdescThis->findByType(VirtualSystemDescriptionType_OS);
1849 if (vsdeOS.size() < 1)
1850 throw setError(VBOX_E_FILE_ERROR,
1851 tr("Missing guest OS type"));
1852 const Utf8Str &strOsTypeVBox = vsdeOS.front()->strVbox;
1853
1854 /* Now that we know the base system get our internal defaults based on that. */
1855 ComPtr<IGuestOSType> osType;
1856 rc = pVirtualBox->GetGuestOSType(Bstr(strOsTypeVBox), osType.asOutParam());
1857 if (FAILED(rc)) throw rc;
1858
1859 /* Create the machine */
1860 /* First get the name */
1861 std::list<VirtualSystemDescriptionEntry*> vsdeName = vsdescThis->findByType(VirtualSystemDescriptionType_Name);
1862 if (vsdeName.size() < 1)
1863 throw setError(VBOX_E_FILE_ERROR,
1864 tr("Missing VM name"));
1865 const Utf8Str &strNameVBox = vsdeName.front()->strVbox;
1866 rc = pVirtualBox->CreateMachine(Bstr(strNameVBox), Bstr(strOsTypeVBox),
1867 Bstr(), Guid(),
1868 pNewMachine.asOutParam());
1869 if (FAILED(rc)) throw rc;
1870
1871 if (!task->progress.isNull())
1872 rc = task->progress->notifyProgress((uint32_t)(opCountMax * opCount++));
1873
1874 /* CPU count (ignored for now) */
1875 // EntriesList vsdeCPU = vsd->findByType (VirtualSystemDescriptionType_CPU);
1876
1877 /* RAM */
1878 std::list<VirtualSystemDescriptionEntry*> vsdeRAM = vsdescThis->findByType(VirtualSystemDescriptionType_Memory);
1879 ComAssertMsgThrow(vsdeRAM.size() == 1, ("RAM size missing"), E_FAIL);
1880 const Utf8Str &memoryVBox = vsdeRAM.front()->strVbox;
1881 ULONG tt = (ULONG)RTStrToUInt64(memoryVBox.c_str());
1882 rc = pNewMachine->COMSETTER(MemorySize)(tt);
1883 if (FAILED(rc)) throw rc;
1884
1885 /* VRAM */
1886 /* Get the recommended VRAM for this guest OS type */
1887 ULONG vramVBox;
1888 rc = osType->COMGETTER(RecommendedVRAM)(&vramVBox);
1889 if (FAILED(rc)) throw rc;
1890
1891 /* Set the VRAM */
1892 rc = pNewMachine->COMSETTER(VRAMSize)(vramVBox);
1893 if (FAILED(rc)) throw rc;
1894
1895 if (!task->progress.isNull())
1896 task->progress->notifyProgress((uint32_t)(opCountMax * opCount++));
1897
1898 /* Audio Adapter */
1899 std::list<VirtualSystemDescriptionEntry*> vsdeAudioAdapter = vsdescThis->findByType(VirtualSystemDescriptionType_SoundCard);
1900 /* @todo: we support one audio adapter only */
1901 if (vsdeAudioAdapter.size() > 0)
1902 {
1903 const Utf8Str& audioAdapterVBox = vsdeAudioAdapter.front()->strVbox;
1904 if (audioAdapterVBox.compare("null", Utf8Str::CaseInsensitive) != 0)
1905 {
1906 uint32_t audio = RTStrToUInt32(audioAdapterVBox.c_str());
1907 ComPtr<IAudioAdapter> audioAdapter;
1908 rc = pNewMachine->COMGETTER(AudioAdapter)(audioAdapter.asOutParam());
1909 if (FAILED(rc)) throw rc;
1910 rc = audioAdapter->COMSETTER(Enabled)(true);
1911 if (FAILED(rc)) throw rc;
1912 rc = audioAdapter->COMSETTER(AudioController)(static_cast<AudioControllerType_T>(audio));
1913 if (FAILED(rc)) throw rc;
1914 }
1915 }
1916
1917#ifdef VBOX_WITH_USB
1918 /* USB Controller */
1919 std::list<VirtualSystemDescriptionEntry*> vsdeUSBController = vsdescThis->findByType(VirtualSystemDescriptionType_USBController);
1920 // USB support is enabled if there's at least one such entry; to disable USB support,
1921 // the type of the USB item would have been changed to "ignore"
1922 bool fUSBEnabled = vsdeUSBController.size() > 0;
1923
1924 ComPtr<IUSBController> usbController;
1925 rc = pNewMachine->COMGETTER(USBController)(usbController.asOutParam());
1926 if (FAILED(rc)) throw rc;
1927 rc = usbController->COMSETTER(Enabled)(fUSBEnabled);
1928 if (FAILED(rc)) throw rc;
1929#endif /* VBOX_WITH_USB */
1930
1931 if (!task->progress.isNull())
1932 task->progress->notifyProgress((uint32_t)(opCountMax * opCount++));
1933
1934 /* Change the network adapters */
1935 std::list<VirtualSystemDescriptionEntry*> vsdeNW = vsdescThis->findByType(VirtualSystemDescriptionType_NetworkAdapter);
1936 if (vsdeNW.size() == 0)
1937 {
1938 /* No network adapters, so we have to disable our default one */
1939 ComPtr<INetworkAdapter> nwVBox;
1940 rc = pNewMachine->GetNetworkAdapter(0, nwVBox.asOutParam());
1941 if (FAILED(rc)) throw rc;
1942 rc = nwVBox->COMSETTER(Enabled)(false);
1943 if (FAILED(rc)) throw rc;
1944 }
1945 else
1946 {
1947 list<VirtualSystemDescriptionEntry*>::const_iterator nwIt;
1948 /* Iterate through all network cards. We support 8 network adapters
1949 * at the maximum. (@todo: warn if there are more!) */
1950 size_t a = 0;
1951 for (nwIt = vsdeNW.begin();
1952 (nwIt != vsdeNW.end() && a < SchemaDefs::NetworkAdapterCount);
1953 ++nwIt, ++a)
1954 {
1955 const VirtualSystemDescriptionEntry* pvsys = *nwIt;
1956
1957 const Utf8Str &nwTypeVBox = pvsys->strVbox;
1958 uint32_t tt1 = RTStrToUInt32(nwTypeVBox.c_str());
1959 ComPtr<INetworkAdapter> pNetworkAdapter;
1960 rc = pNewMachine->GetNetworkAdapter((ULONG)a, pNetworkAdapter.asOutParam());
1961 if (FAILED(rc)) throw rc;
1962 /* Enable the network card & set the adapter type */
1963 rc = pNetworkAdapter->COMSETTER(Enabled)(true);
1964 if (FAILED(rc)) throw rc;
1965 rc = pNetworkAdapter->COMSETTER(AdapterType)(static_cast<NetworkAdapterType_T>(tt1));
1966 if (FAILED(rc)) throw rc;
1967
1968 // default is NAT; change to "bridged" if extra conf says so
1969 if (!pvsys->strExtraConfig.compare("type=Bridged", Utf8Str::CaseInsensitive))
1970 {
1971 rc = pNetworkAdapter->AttachToBridgedInterface();
1972 if (FAILED(rc)) throw rc;
1973 }
1974 }
1975 }
1976
1977 /* Floppy drive */
1978 std::list<VirtualSystemDescriptionEntry*> vsdeFloppy = vsdescThis->findByType(VirtualSystemDescriptionType_Floppy);
1979 // Floppy support is enabled if there's at least one such entry; to disable floppy support,
1980 // the type of the floppy item would have been changed to "ignore"
1981 bool fFloppyEnabled = vsdeFloppy.size() > 0;
1982 ComPtr<IFloppyDrive> floppyDrive;
1983 rc = pNewMachine->COMGETTER(FloppyDrive)(floppyDrive.asOutParam());
1984 if (FAILED(rc)) throw rc;
1985 rc = floppyDrive->COMSETTER(Enabled)(fFloppyEnabled);
1986 if (FAILED(rc)) throw rc;
1987
1988 if (!task->progress.isNull())
1989 task->progress->notifyProgress((uint32_t)(opCountMax * opCount++));
1990
1991 /* CDROM drive */
1992 /* @todo: I can't disable the CDROM. So nothing to do for now */
1993 // std::list<VirtualSystemDescriptionEntry*> vsdeFloppy = vsd->findByType(VirtualSystemDescriptionType_CDROM);
1994
1995 /* Hard disk controller IDE */
1996 std::list<VirtualSystemDescriptionEntry*> vsdeHDCIDE = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerIDE);
1997 if (vsdeHDCIDE.size() > 1)
1998 throw setError(VBOX_E_FILE_ERROR,
1999 tr("Too many IDE controllers in OVF; VirtualBox only supports one"));
2000 if (vsdeHDCIDE.size() == 1)
2001 {
2002 ComPtr<IStorageController> pController;
2003 rc = pNewMachine->GetStorageControllerByName(Bstr("IDE"), pController.asOutParam());
2004 if (FAILED(rc)) throw rc;
2005
2006 const char *pcszIDEType = vsdeHDCIDE.front()->strVbox.c_str();
2007 if (!strcmp(pcszIDEType, "PIIX3"))
2008 rc = pController->COMSETTER(ControllerType)(StorageControllerType_PIIX3);
2009 else if (!strcmp(pcszIDEType, "PIIX4"))
2010 rc = pController->COMSETTER(ControllerType)(StorageControllerType_PIIX4);
2011 else if (!strcmp(pcszIDEType, "ICH6"))
2012 rc = pController->COMSETTER(ControllerType)(StorageControllerType_ICH6);
2013 else
2014 throw setError(VBOX_E_FILE_ERROR,
2015 tr("Invalid IDE controller type \"%s\""),
2016 pcszIDEType);
2017 if (FAILED(rc)) throw rc;
2018 }
2019#ifdef VBOX_WITH_AHCI
2020 /* Hard disk controller SATA */
2021 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSATA = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSATA);
2022 if (vsdeHDCSATA.size() > 1)
2023 throw setError(VBOX_E_FILE_ERROR,
2024 tr("Too many SATA controllers in OVF; VirtualBox only supports one"));
2025 if (vsdeHDCSATA.size() > 0)
2026 {
2027 ComPtr<IStorageController> pController;
2028 const Utf8Str &hdcVBox = vsdeHDCSATA.front()->strVbox;
2029 if (hdcVBox == "AHCI")
2030 {
2031 rc = pNewMachine->AddStorageController(Bstr("SATA"), StorageBus_SATA, pController.asOutParam());
2032 if (FAILED(rc)) throw rc;
2033 }
2034 else
2035 throw setError(VBOX_E_FILE_ERROR,
2036 tr("Invalid SATA controller type \"%s\""),
2037 hdcVBox.c_str());
2038 }
2039#endif /* VBOX_WITH_AHCI */
2040
2041 /* Hard disk controller SCSI */
2042 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSCSI = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSCSI);
2043 if (vsdeHDCSCSI.size() > 1)
2044 throw setError(VBOX_E_FILE_ERROR,
2045 tr("Too many SCSI controllers in OVF; VirtualBox only supports one"));
2046 if (vsdeHDCSCSI.size() > 0)
2047 {
2048 ComPtr<IStorageController> pController;
2049 StorageControllerType_T controllerType;
2050 const Utf8Str &hdcVBox = vsdeHDCSCSI.front()->strVbox;
2051 if (hdcVBox == "LsiLogic")
2052 controllerType = StorageControllerType_LsiLogic;
2053 else if (hdcVBox == "BusLogic")
2054 controllerType = StorageControllerType_BusLogic;
2055 else
2056 throw setError(VBOX_E_FILE_ERROR,
2057 tr("Invalid SCSI controller type \"%s\""),
2058 hdcVBox.c_str());
2059
2060 rc = pNewMachine->AddStorageController(Bstr("SCSI"), StorageBus_SCSI, pController.asOutParam());
2061 if (FAILED(rc)) throw rc;
2062 rc = pController->COMSETTER(ControllerType)(controllerType);
2063 if (FAILED(rc)) throw rc;
2064 }
2065
2066 /* Now its time to register the machine before we add any hard disks */
2067 rc = pVirtualBox->RegisterMachine(pNewMachine);
2068 if (FAILED(rc)) throw rc;
2069
2070 Guid newMachineId;
2071 rc = pNewMachine->COMGETTER(Id)(newMachineId.asOutParam());
2072 if (FAILED(rc)) throw rc;
2073
2074 if (!task->progress.isNull())
2075 task->progress->notifyProgress((uint32_t)(opCountMax * opCount++));
2076
2077 // store new machine for roll-back in case of errors
2078 llMachinesRegistered.push_back(newMachineId);
2079
2080 /* Create the hard disks & connect them to the appropriate controllers. */
2081 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
2082 if (avsdeHDs.size() > 0)
2083 {
2084 /* If in the next block an error occur we have to deregister
2085 the machine, so make an extra try/catch block. */
2086 ComPtr<IHardDisk> srcHdVBox;
2087 bool fSourceHdNeedsClosing = false;
2088
2089 try
2090 {
2091 /* In order to attach hard disks we need to open a session
2092 * for the new machine */
2093 rc = pVirtualBox->OpenSession(session, newMachineId);
2094 if (FAILED(rc)) throw rc;
2095 fSessionOpen = true;
2096
2097 /* The disk image has to be on the same place as the OVF file. So
2098 * strip the filename out of the full file path. */
2099 Utf8Str strSrcDir = stripFilename(pAppliance->m->strPath);
2100
2101 /* Iterate over all given disk images */
2102 list<VirtualSystemDescriptionEntry*>::const_iterator itHD;
2103 for (itHD = avsdeHDs.begin();
2104 itHD != avsdeHDs.end();
2105 ++itHD)
2106 {
2107 VirtualSystemDescriptionEntry *vsdeHD = *itHD;
2108
2109 const char *pcszDstFilePath = vsdeHD->strVbox.c_str();
2110 /* Check if the destination file exists already or the
2111 * destination path is empty. */
2112 if ( !(*pcszDstFilePath)
2113 || RTPathExists(pcszDstFilePath)
2114 )
2115 /* This isn't allowed */
2116 throw setError(VBOX_E_FILE_ERROR,
2117 tr("Destination file '%s' exists",
2118 pcszDstFilePath));
2119
2120 /* Find the disk from the OVF's disk list */
2121 DiskImagesMap::const_iterator itDiskImage = pAppliance->m->mapDisks.find(vsdeHD->strRef);
2122 /* vsdeHD->strRef contains the disk identifier (e.g. "vmdisk1"), which should exist
2123 in the virtual system's disks map under that ID and also in the global images map. */
2124 VirtualDisksMap::const_iterator itVirtualDisk = vsysThis.mapVirtualDisks.find(vsdeHD->strRef);
2125
2126 if ( itDiskImage == pAppliance->m->mapDisks.end()
2127 || itVirtualDisk == vsysThis.mapVirtualDisks.end()
2128 )
2129 throw setError(E_FAIL,
2130 tr("Internal inconsistency looking up disk images."));
2131
2132 const DiskImage &di = itDiskImage->second;
2133 const VirtualDisk &vd = itVirtualDisk->second;
2134
2135 /* Make sure all target directories exists */
2136 rc = VirtualBox::ensureFilePathExists(pcszDstFilePath);
2137 if (FAILED(rc))
2138 throw rc;
2139
2140 ComPtr<IProgress> progress;
2141
2142 ComPtr<IHardDisk> dstHdVBox;
2143 /* If strHref is empty we have to create a new file */
2144 if (di.strHref.isEmpty())
2145 {
2146 /* Which format to use? */
2147 Bstr srcFormat = L"VDI";
2148 if ( di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#sparse", Utf8Str::CaseInsensitive)
2149 || di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive))
2150 srcFormat = L"VMDK";
2151 /* Create an empty hard disk */
2152 rc = pVirtualBox->CreateHardDisk(srcFormat, Bstr(pcszDstFilePath), dstHdVBox.asOutParam());
2153 if (FAILED(rc)) throw rc;
2154
2155 /* Create a dynamic growing disk image with the given capacity */
2156 rc = dstHdVBox->CreateDynamicStorage(di.iCapacity / _1M, HardDiskVariant_Standard, progress.asOutParam());
2157 if (FAILED(rc)) throw rc;
2158
2159 /* Advance to the next operation */
2160 if (!task->progress.isNull())
2161 task->progress->advanceOperation (BstrFmt(tr("Creating virtual disk image '%s'"), pcszDstFilePath));
2162 }
2163 else
2164 {
2165 /* Construct the source file path */
2166 Utf8StrFmt strSrcFilePath("%s%c%s", strSrcDir.c_str(), RTPATH_DELIMITER, di.strHref.c_str());
2167 /* Check if the source file exists */
2168 if (!RTPathExists(strSrcFilePath.c_str()))
2169 /* This isn't allowed */
2170 throw setError(VBOX_E_FILE_ERROR,
2171 tr("Source virtual disk image file '%s' doesn't exist"),
2172 strSrcFilePath.c_str());
2173
2174 /* Clone the disk image (this is necessary cause the id has
2175 * to be recreated for the case the same hard disk is
2176 * attached already from a previous import) */
2177
2178 /* First open the existing disk image */
2179 rc = pVirtualBox->OpenHardDisk(Bstr(strSrcFilePath), srcHdVBox.asOutParam());
2180 if (FAILED(rc)) throw rc;
2181 fSourceHdNeedsClosing = true;
2182
2183 /* We need the format description of the source disk image */
2184 Bstr srcFormat;
2185 rc = srcHdVBox->COMGETTER(Format)(srcFormat.asOutParam());
2186 if (FAILED(rc)) throw rc;
2187 /* Create a new hard disk interface for the destination disk image */
2188 rc = pVirtualBox->CreateHardDisk(srcFormat, Bstr(pcszDstFilePath), dstHdVBox.asOutParam());
2189 if (FAILED(rc)) throw rc;
2190 /* Clone the source disk image */
2191 rc = srcHdVBox->CloneTo(dstHdVBox, HardDiskVariant_Standard, progress.asOutParam());
2192 if (FAILED(rc)) throw rc;
2193
2194 /* Advance to the next operation */
2195 if (!task->progress.isNull())
2196 task->progress->advanceOperation (BstrFmt(tr("Importing virtual disk image '%s'"), strSrcFilePath.c_str()));
2197 }
2198
2199 // now loop until the asynchronous operation completes and then
2200 // report its result
2201 BOOL fCompleted;
2202 LONG currentPercent;
2203 while (SUCCEEDED(progress->COMGETTER(Completed(&fCompleted))))
2204 {
2205 rc = progress->COMGETTER(Percent(&currentPercent));
2206 if (FAILED(rc)) throw rc;
2207 if (!task->progress.isNull())
2208 task->progress->notifyProgress(currentPercent);
2209 if (fCompleted)
2210 break;
2211 /* Make sure the loop is not too tight */
2212 rc = progress->WaitForCompletion(100);
2213 if (FAILED(rc)) throw rc;
2214 }
2215 // report result of asynchronous operation
2216 HRESULT vrc;
2217 rc = progress->COMGETTER(ResultCode)(&vrc);
2218 if (FAILED(rc)) throw rc;
2219
2220 // if the thread of the progress object has an error, then
2221 // retrieve the error info from there, or it'll be lost
2222 if (FAILED(vrc))
2223 {
2224 ProgressErrorInfo info(progress);
2225 Utf8Str str(info.getText());
2226 const char *pcsz = str.c_str();
2227 HRESULT rc2 = setError(vrc,
2228 pcsz);
2229 throw rc2;
2230 }
2231
2232 if (fSourceHdNeedsClosing)
2233 {
2234 rc = srcHdVBox->Close();
2235 if (FAILED(rc)) throw rc;
2236 fSourceHdNeedsClosing = false;
2237 }
2238
2239 llHardDisksCreated.push_back(dstHdVBox);
2240
2241 /* Now use the new uuid to attach the disk image to our new machine */
2242 ComPtr<IMachine> sMachine;
2243 rc = session->COMGETTER(Machine)(sMachine.asOutParam());
2244 if (FAILED(rc)) throw rc;
2245 Guid hdId;
2246 rc = dstHdVBox->COMGETTER(Id)(hdId.asOutParam());
2247 if (FAILED(rc)) throw rc;
2248
2249 /* For now we assume we have one controller of every type only */
2250 HardDiskController hdc = (*vsysThis.mapControllers.find(vd.idController)).second;
2251
2252 // this is for rollback later
2253 MyHardDiskAttachment mhda;
2254 mhda.uuid = newMachineId;
2255 mhda.pMachine = pNewMachine;
2256
2257 switch (hdc.system)
2258 {
2259 case HardDiskController::IDE:
2260 // For the IDE bus, the channel parameter can be either 0 or 1, to specify the primary
2261 // or secondary IDE controller, respectively. For the primary controller of the IDE bus,
2262 // the device number can be either 0 or 1, to specify the master or the slave device,
2263 // respectively. For the secondary IDE controller, the device number is always 1 because
2264 // the master device is reserved for the CD-ROM drive.
2265 mhda.controllerType = Bstr("IDE");
2266 switch (vd.ulAddressOnParent)
2267 {
2268 case 0: // interpret this as primary master
2269 mhda.lChannel = (long)0;
2270 mhda.lDevice = (long)0;
2271 break;
2272
2273 case 1: // interpret this as primary slave
2274 mhda.lChannel = (long)0;
2275 mhda.lDevice = (long)1;
2276 break;
2277
2278 case 2: // interpret this as secondary slave
2279 mhda.lChannel = (long)1;
2280 mhda.lDevice = (long)1;
2281 break;
2282
2283 default:
2284 throw setError(VBOX_E_NOT_SUPPORTED,
2285 tr("Invalid channel %RI16 specified; IDE controllers support only 0, 1 or 2"), vd.ulAddressOnParent);
2286 break;
2287 }
2288 break;
2289
2290 case HardDiskController::SATA:
2291 mhda.controllerType = Bstr("SATA");
2292 mhda.lChannel = (long)vd.ulAddressOnParent;
2293 mhda.lDevice = (long)0;
2294 break;
2295
2296 case HardDiskController::SCSI:
2297 mhda.controllerType = Bstr("SCSI");
2298 mhda.lChannel = (long)vd.ulAddressOnParent;
2299 mhda.lDevice = (long)0;
2300 break;
2301
2302 default: break;
2303 }
2304
2305 Log(("Attaching disk %s to channel %d on device %d\n", pcszDstFilePath, mhda.lChannel, mhda.lDevice));
2306
2307 rc = sMachine->AttachHardDisk(hdId,
2308 mhda.controllerType,
2309 mhda.lChannel,
2310 mhda.lDevice);
2311 if (FAILED(rc)) throw rc;
2312
2313 llHardDiskAttachments.push_back(mhda);
2314
2315 rc = sMachine->SaveSettings();
2316 if (FAILED(rc)) throw rc;
2317 } // end for (itHD = avsdeHDs.begin();
2318
2319 // only now that we're done with all disks, close the session
2320 rc = session->Close();
2321 if (FAILED(rc)) throw rc;
2322 fSessionOpen = false;
2323 }
2324 catch(HRESULT /* aRC */)
2325 {
2326 if (fSourceHdNeedsClosing)
2327 srcHdVBox->Close();
2328
2329 if (fSessionOpen)
2330 session->Close();
2331
2332 throw;
2333 }
2334 }
2335 }
2336 catch(HRESULT aRC)
2337 {
2338 rc = aRC;
2339 }
2340
2341 if (FAILED(rc))
2342 break;
2343
2344 } // for (it = pAppliance->m->llVirtualSystems.begin(),
2345
2346 if (FAILED(rc))
2347 {
2348 // with _whatever_ error we've had, do a complete roll-back of
2349 // machines and disks we've created; unfortunately this is
2350 // not so trivially done...
2351
2352 HRESULT rc2;
2353 // detach all hard disks from all machines we created
2354 list<MyHardDiskAttachment>::iterator itM;
2355 for (itM = llHardDiskAttachments.begin();
2356 itM != llHardDiskAttachments.end();
2357 ++itM)
2358 {
2359 const MyHardDiskAttachment &mhda = *itM;
2360 rc2 = pVirtualBox->OpenSession(session, mhda.uuid);
2361 if (SUCCEEDED(rc2))
2362 {
2363 ComPtr<IMachine> sMachine;
2364 rc2 = session->COMGETTER(Machine)(sMachine.asOutParam());
2365 if (SUCCEEDED(rc2))
2366 {
2367 rc2 = sMachine->DetachHardDisk(Bstr(mhda.controllerType), mhda.lChannel, mhda.lDevice);
2368 rc2 = sMachine->SaveSettings();
2369 }
2370 session->Close();
2371 }
2372 }
2373
2374 // now clean up all hard disks we created
2375 list< ComPtr<IHardDisk> >::iterator itHD;
2376 for (itHD = llHardDisksCreated.begin();
2377 itHD != llHardDisksCreated.end();
2378 ++itHD)
2379 {
2380 ComPtr<IHardDisk> pDisk = *itHD;
2381 ComPtr<IProgress> pProgress;
2382 rc2 = pDisk->DeleteStorage(pProgress.asOutParam());
2383 rc2 = pProgress->WaitForCompletion(-1);
2384 }
2385
2386 // finally, deregister and remove all machines
2387 list<Guid>::iterator itID;
2388 for (itID = llMachinesRegistered.begin();
2389 itID != llMachinesRegistered.end();
2390 ++itID)
2391 {
2392 const Guid &guid = *itID;
2393 ComPtr<IMachine> failedMachine;
2394 rc2 = pVirtualBox->UnregisterMachine(guid, failedMachine.asOutParam());
2395 if (SUCCEEDED(rc2))
2396 rc2 = failedMachine->DeleteSettings();
2397 }
2398 }
2399
2400 task->rc = rc;
2401
2402 if (!task->progress.isNull())
2403 task->progress->notifyComplete(rc);
2404
2405 LogFlowFunc(("rc=%Rhrc\n", rc));
2406 LogFlowFuncLeave();
2407
2408 return VINF_SUCCESS;
2409}
2410
2411/**
2412 * Worker thread implementation for Write() (ovf writer).
2413 * @param aThread
2414 * @param pvUser
2415 */
2416/* static */
2417DECLCALLBACK(int) Appliance::taskThreadWriteOVF(RTTHREAD /* aThread */, void *pvUser)
2418{
2419 std::auto_ptr<TaskWriteOVF> task(static_cast<TaskWriteOVF*>(pvUser));
2420 AssertReturn(task.get(), VERR_GENERAL_FAILURE);
2421
2422 Appliance *pAppliance = task->pAppliance;
2423
2424 LogFlowFuncEnter();
2425 LogFlowFunc(("Appliance %p\n", pAppliance));
2426
2427 AutoCaller autoCaller(pAppliance);
2428 CheckComRCReturnRC(autoCaller.rc());
2429
2430 AutoWriteLock appLock(pAppliance);
2431
2432 HRESULT rc = S_OK;
2433
2434 ComPtr<IVirtualBox> pVirtualBox(pAppliance->mVirtualBox);
2435
2436 try
2437 {
2438 xml::Document doc;
2439 xml::ElementNode *pelmRoot = doc.createRootElement("Envelope");
2440
2441 pelmRoot->setAttribute("ovf:version", "1.0");
2442 pelmRoot->setAttribute("xml:lang", "en-US");
2443 pelmRoot->setAttribute("xmlns", "http://schemas.dmtf.org/ovf/envelope/1");
2444 pelmRoot->setAttribute("xmlns:ovf", "http://schemas.dmtf.org/ovf/envelope/1");
2445 pelmRoot->setAttribute("xmlns:ovfstr", "http://schema.dmtf.org/ovf/strings/1");
2446 pelmRoot->setAttribute("xmlns:rasd", "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData");
2447 pelmRoot->setAttribute("xmlns:vssd", "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData");
2448 pelmRoot->setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
2449 pelmRoot->setAttribute("xsi:schemaLocation", "http://schemas.dmtf.org/ovf/envelope/1 ../ovf-envelope.xsd");
2450
2451
2452 // <Envelope>/<References>
2453 xml::ElementNode *pelmReferences = pelmRoot->createChild("References");
2454 // @ŧodo
2455
2456 /* <Envelope>/<DiskSection>:
2457 <DiskSection>
2458 <Info>List of the virtual disks used in the package</Info>
2459 <Disk ovf:capacity="4294967296" ovf:diskId="lamp" ovf:format="http://www.vmware.com/specifications/vmdk.html#compressed" ovf:populatedSize="1924967692"/>
2460 </DiskSection> */
2461 xml::ElementNode *pelmDiskSection = pelmRoot->createChild("DiskSection");
2462 xml::ElementNode *pelmDiskSectionInfo = pelmDiskSection->createChild("Info");
2463 pelmDiskSectionInfo->addContent("List of the virtual disks used in the package");
2464 // for now, set up a map so we have a list of unique disk names (to make
2465 // sure the same disk name is only added once)
2466 map<Utf8Str, const VirtualSystemDescriptionEntry*> mapDisks;
2467
2468 /* <Envelope>/<NetworkSection>:
2469 <NetworkSection>
2470 <Info>Logical networks used in the package</Info>
2471 <Network ovf:name="VM Network">
2472 <Description>The network that the LAMP Service will be available on</Description>
2473 </Network>
2474 </NetworkSection> */
2475 xml::ElementNode *pelmNetworkSection = pelmRoot->createChild("NetworkSection");
2476 xml::ElementNode *pelmNetworkSectionInfo = pelmNetworkSection->createChild("Info");
2477 pelmNetworkSectionInfo->addContent("Logical networks used in the package");
2478 // for now, set up a map so we have a list of unique network names (to make
2479 // sure the same network name is only added once)
2480 map<Utf8Str, bool> mapNetworks;
2481 // we fill this later below when we iterate over the networks
2482
2483 // and here come the virtual systems:
2484 xml::ElementNode *pelmVirtualSystemCollection = pelmRoot->createChild("VirtualSystemCollection");
2485 /* xml::AttributeNode *pattrVirtualSystemCollectionId = */ pelmVirtualSystemCollection->setAttribute("ovf:id", "ExportedVirtualBoxMachines"); // whatever
2486
2487 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;
2488 /* Iterate through all virtual systems of that appliance */
2489 for (it = pAppliance->m->virtualSystemDescriptions.begin();
2490 it != pAppliance->m->virtualSystemDescriptions.end();
2491 ++it)
2492 {
2493 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);
2494
2495 xml::ElementNode *pelmVirtualSystem = pelmVirtualSystemCollection->createChild("VirtualSystem");
2496 /* xml::ElementNode *pelmVirtualSystemInfo = */ pelmVirtualSystem->createChild("Info"); // @todo put in description here after implementing an entry for it
2497
2498 std::list<VirtualSystemDescriptionEntry*> llName = vsdescThis->findByType(VirtualSystemDescriptionType_Name);
2499 if (llName.size() != 1)
2500 throw setError(VBOX_E_NOT_SUPPORTED,
2501 tr("Missing VM name"));
2502 pelmVirtualSystem->setAttribute("ovf:id", llName.front()->strVbox);
2503
2504 std::list<VirtualSystemDescriptionEntry*> llOS = vsdescThis->findByType(VirtualSystemDescriptionType_OS);
2505 if (llOS.size() != 1)
2506 throw setError(VBOX_E_NOT_SUPPORTED,
2507 tr("Missing OS type"));
2508 /* <OperatingSystemSection ovf:id="82">
2509 <Info>Guest Operating System</Info>
2510 <Description>Linux 2.6.x</Description>
2511 </OperatingSystemSection> */
2512 xml::ElementNode *pelmOperatingSystemSection = pelmVirtualSystem->createChild("OperatingSystemSection");
2513 pelmOperatingSystemSection->setAttribute("ovf:id", llOS.front()->strOvf);
2514 pelmOperatingSystemSection->createChild("Info")->addContent("blah"); // @ŧodo
2515 pelmOperatingSystemSection->createChild("Description")->addContent("blah"); // @ŧodo
2516
2517 // <VirtualHardwareSection ovf:id="hw1" ovf:transport="iso">
2518 xml::ElementNode *pelmVirtualHardwareSection = pelmVirtualSystem->createChild("VirtualHardwareSection");
2519
2520 /* <System>
2521 <vssd:Description>Description of the virtual hardware section.</vssd:Description>
2522 <vssd:ElementName>vmware</vssd:ElementName>
2523 <vssd:InstanceID>1</vssd:InstanceID>
2524 <vssd:VirtualSystemIdentifier>MyLampService</vssd:VirtualSystemIdentifier>
2525 <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType>
2526 </System> */
2527 xml::ElementNode *pelmSystem = pelmVirtualHardwareSection->createChild("System");
2528
2529 // <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType>
2530 xml::ElementNode *pelmVirtualSystemType = pelmSystem->createChild("VirtualSystemType");
2531 pelmVirtualSystemType->addContent("virtualbox-2.2"); // instead of vmx-7?
2532
2533 // loop thru all description entries twice; once to write out all
2534 // devices _except_ disk images, and a second time to assign the
2535 // disk images; this is because disk images need to reference
2536 // IDE controllers, and we can't know their instance IDs without
2537 // assigning them first
2538
2539 uint32_t idIDEController = 0;
2540 int32_t lIDEControllerIndex = 0;
2541 uint32_t idSATAController = 0;
2542 int32_t lSATAControllerIndex = 0;
2543 uint32_t idSCSIController = 0;
2544 int32_t lSCSIControllerIndex = 0;
2545
2546 uint32_t ulInstanceID = 1;
2547 uint32_t cDisks = 0;
2548
2549 for (size_t uLoop = 1;
2550 uLoop <= 2;
2551 ++uLoop)
2552 {
2553 int32_t lIndexThis = 0;
2554 list<VirtualSystemDescriptionEntry>::const_iterator itD;
2555 for (itD = vsdescThis->m->llDescriptions.begin();
2556 itD != vsdescThis->m->llDescriptions.end();
2557 ++itD, ++lIndexThis)
2558 {
2559 const VirtualSystemDescriptionEntry &desc = *itD;
2560
2561 OVFResourceType_T type = (OVFResourceType_T)0; // if this becomes != 0 then we do stuff
2562 Utf8Str strResourceSubType;
2563
2564 Utf8Str strDescription; // results in <rasd:Description>...</rasd:Description> block
2565 Utf8Str strCaption; // results in <rasd:Caption>...</rasd:Caption> block
2566
2567 uint32_t ulParent = 0;
2568
2569 int32_t lVirtualQuantity = -1;
2570 Utf8Str strAllocationUnits;
2571
2572 int32_t lAddress = -1;
2573 int32_t lBusNumber = -1;
2574 int32_t lAddressOnParent = -1;
2575
2576 int32_t lAutomaticAllocation = -1; // 0 means "false", 1 means "true"
2577 Utf8Str strConnection; // results in <rasd:Connection>...</rasd:Connection> block
2578 Utf8Str strHostResource;
2579
2580 uint64_t uTemp;
2581
2582 switch (desc.type)
2583 {
2584 case VirtualSystemDescriptionType_CPU:
2585 /* <Item>
2586 <rasd:Caption>1 virtual CPU</rasd:Caption>
2587 <rasd:Description>Number of virtual CPUs</rasd:Description>
2588 <rasd:ElementName>virtual CPU</rasd:ElementName>
2589 <rasd:InstanceID>1</rasd:InstanceID>
2590 <rasd:ResourceType>3</rasd:ResourceType>
2591 <rasd:VirtualQuantity>1</rasd:VirtualQuantity>
2592 </Item> */
2593 if (uLoop == 1)
2594 {
2595 strDescription = "Number of virtual CPUs";
2596 type = OVFResourceType_Processor; // 3
2597 lVirtualQuantity = 1;
2598 }
2599 break;
2600
2601 case VirtualSystemDescriptionType_Memory:
2602 /* <Item>
2603 <rasd:AllocationUnits>MegaBytes</rasd:AllocationUnits>
2604 <rasd:Caption>256 MB of memory</rasd:Caption>
2605 <rasd:Description>Memory Size</rasd:Description>
2606 <rasd:ElementName>Memory</rasd:ElementName>
2607 <rasd:InstanceID>2</rasd:InstanceID>
2608 <rasd:ResourceType>4</rasd:ResourceType>
2609 <rasd:VirtualQuantity>256</rasd:VirtualQuantity>
2610 </Item> */
2611 if (uLoop == 1)
2612 {
2613 strDescription = "Memory Size";
2614 type = OVFResourceType_Memory; // 4
2615 desc.strVbox.toInt(uTemp);
2616 lVirtualQuantity = (int32_t)(uTemp / _1M);
2617 strAllocationUnits = "MegaBytes";
2618 }
2619 break;
2620
2621 case VirtualSystemDescriptionType_HardDiskControllerIDE:
2622 /* <Item>
2623 <rasd:Caption>ideController1</rasd:Caption>
2624 <rasd:Description>IDE Controller</rasd:Description>
2625 <rasd:InstanceId>5</rasd:InstanceId>
2626 <rasd:ResourceType>5</rasd:ResourceType>
2627 <rasd:Address>1</rasd:Address>
2628 <rasd:BusNumber>1</rasd:BusNumber>
2629 </Item> */
2630 if (uLoop == 1)
2631 {
2632 strDescription = "IDE Controller";
2633 type = OVFResourceType_IdeController; // 5
2634 // it seems that OVFTool always writes these two, and since we can only
2635 // have one IDE controller, we'll use this as well
2636 lAddress = 1;
2637 lBusNumber = 1;
2638
2639 // remember this ID
2640 idIDEController = ulInstanceID;
2641 lIDEControllerIndex = lIndexThis;
2642 }
2643 break;
2644
2645 case VirtualSystemDescriptionType_HardDiskControllerSATA:
2646 /* <Item>
2647 <rasd:Caption>sataController0</rasd:Caption>
2648 <rasd:Description>SATA Controller</rasd:Description>
2649 <rasd:InstanceId>4</rasd:InstanceId>
2650 <rasd:ResourceType>20</rasd:ResourceType>
2651 <rasd:ResourceSubType>ahci</rasd:ResourceSubType>
2652 <rasd:Address>0</rasd:Address>
2653 <rasd:BusNumber>0</rasd:BusNumber>
2654 </Item>
2655 */
2656 if (uLoop == 1)
2657 {
2658 strDescription = "SATA Controller";
2659 strCaption = "sataController0";
2660 type = OVFResourceType_OtherStorageDevice; // 20
2661 // it seems that OVFTool always writes these two, and since we can only
2662 // have one SATA controller, we'll use this as well
2663 lAddress = 0;
2664 lBusNumber = 0;
2665
2666 if ( desc.strVbox.isEmpty() // AHCI is the default in VirtualBox
2667 || (!desc.strVbox.compare("ahci", Utf8Str::CaseInsensitive))
2668 )
2669 strResourceSubType = "AHCI";
2670 else
2671 throw setError(VBOX_E_NOT_SUPPORTED,
2672 tr("Invalid config string \"%s\" in SATA controller"), desc.strVbox.c_str());
2673
2674 // remember this ID
2675 idSATAController = ulInstanceID;
2676 lSATAControllerIndex = lIndexThis;
2677 }
2678 break;
2679
2680 case VirtualSystemDescriptionType_HardDiskControllerSCSI:
2681 /* <Item>
2682 <rasd:Caption>scsiController0</rasd:Caption>
2683 <rasd:Description>SCSI Controller</rasd:Description>
2684 <rasd:InstanceId>4</rasd:InstanceId>
2685 <rasd:ResourceType>6</rasd:ResourceType>
2686 <rasd:ResourceSubType>buslogic</rasd:ResourceSubType>
2687 <rasd:Address>0</rasd:Address>
2688 <rasd:BusNumber>0</rasd:BusNumber>
2689 </Item>
2690 */
2691 if (uLoop == 1)
2692 {
2693 strDescription = "SCSI Controller";
2694 strCaption = "scsiController0";
2695 type = OVFResourceType_ParallelScsiHba; // 6
2696 // it seems that OVFTool always writes these two, and since we can only
2697 // have one SATA controller, we'll use this as well
2698 lAddress = 0;
2699 lBusNumber = 0;
2700
2701 if ( desc.strVbox.isEmpty() // LsiLogic is the default in VirtualBox
2702 || (!desc.strVbox.compare("lsilogic", Utf8Str::CaseInsensitive))
2703 )
2704 strResourceSubType = "lsilogic";
2705 else if (!desc.strVbox.compare("buslogic", Utf8Str::CaseInsensitive))
2706 strResourceSubType = "buslogic";
2707 else
2708 throw setError(VBOX_E_NOT_SUPPORTED,
2709 tr("Invalid config string \"%s\" in SCSI controller"), desc.strVbox.c_str());
2710
2711 // remember this ID
2712 idSCSIController = ulInstanceID;
2713 lSCSIControllerIndex = lIndexThis;
2714 }
2715 break;
2716
2717 case VirtualSystemDescriptionType_HardDiskImage:
2718 /* <Item>
2719 <rasd:Caption>disk1</rasd:Caption>
2720 <rasd:InstanceId>8</rasd:InstanceId>
2721 <rasd:ResourceType>17</rasd:ResourceType>
2722 <rasd:HostResource>/disk/vmdisk1</rasd:HostResource>
2723 <rasd:Parent>4</rasd:Parent>
2724 <rasd:AddressOnParent>0</rasd:AddressOnParent>
2725 </Item> */
2726 if (uLoop == 2)
2727 {
2728 Utf8Str strDiskID = Utf8StrFmt("vmdisk%RI32", ++cDisks);
2729
2730 strDescription = "Disk Image";
2731 strCaption = Utf8StrFmt("disk%RI32", cDisks); // this is not used for anything else
2732 type = OVFResourceType_HardDisk; // 17
2733
2734 // the following references the "<Disks>" XML block
2735 strHostResource = Utf8StrFmt("/disk/%s", strDiskID.c_str());
2736
2737 // controller=<index>;channel=<c>
2738 size_t pos1 = desc.strExtraConfig.find("controller=");
2739 size_t pos2 = desc.strExtraConfig.find("channel=");
2740 if (pos1 != Utf8Str::npos)
2741 {
2742 int32_t lControllerIndex = -1;
2743 RTStrToInt32Ex(desc.strExtraConfig.c_str() + pos1 + 11, NULL, 0, &lControllerIndex);
2744 if (lControllerIndex == lIDEControllerIndex)
2745 ulParent = idIDEController;
2746 else if (lControllerIndex == lSCSIControllerIndex)
2747 ulParent = idSCSIController;
2748 else if (lControllerIndex == lSATAControllerIndex)
2749 ulParent = idSATAController;
2750 }
2751 if (pos2 != Utf8Str::npos)
2752 RTStrToInt32Ex(desc.strExtraConfig.c_str() + pos2 + 8, NULL, 0, &lAddressOnParent);
2753
2754 if ( !ulParent
2755 || lAddressOnParent == -1
2756 )
2757 throw setError(VBOX_E_NOT_SUPPORTED,
2758 tr("Missing or bad extra config string in hard disk image: \"%s\""), desc.strExtraConfig.c_str());
2759
2760 mapDisks[strDiskID] = &desc;
2761 }
2762 break;
2763
2764 case VirtualSystemDescriptionType_Floppy:
2765 if (uLoop == 1)
2766 {
2767 strDescription = "Floppy Drive";
2768 strCaption = "floppy0"; // this is what OVFTool writes
2769 type = OVFResourceType_FloppyDrive; // 14
2770 lAutomaticAllocation = 0;
2771 lAddressOnParent = 0; // this is what OVFTool writes
2772 }
2773 break;
2774
2775 case VirtualSystemDescriptionType_CDROM:
2776 if (uLoop == 2)
2777 {
2778 // we can't have a CD without an IDE controller
2779 if (!idIDEController)
2780 throw setError(VBOX_E_NOT_SUPPORTED,
2781 tr("Can't have CD-ROM without IDE controller"));
2782
2783 strDescription = "CD-ROM Drive";
2784 strCaption = "cdrom1"; // this is what OVFTool writes
2785 type = OVFResourceType_CdDrive; // 15
2786 lAutomaticAllocation = 1;
2787 ulParent = idIDEController;
2788 lAddressOnParent = 0; // this is what OVFTool writes
2789 }
2790 break;
2791
2792 case VirtualSystemDescriptionType_NetworkAdapter:
2793 /* <Item>
2794 <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
2795 <rasd:Caption>Ethernet adapter on 'VM Network'</rasd:Caption>
2796 <rasd:Connection>VM Network</rasd:Connection>
2797 <rasd:ElementName>VM network</rasd:ElementName>
2798 <rasd:InstanceID>3</rasd:InstanceID>
2799 <rasd:ResourceType>10</rasd:ResourceType>
2800 </Item> */
2801 if (uLoop == 1)
2802 {
2803 lAutomaticAllocation = 1;
2804 strCaption = Utf8StrFmt("Ethernet adapter on '%s'", desc.strOvf.c_str());
2805 type = OVFResourceType_EthernetAdapter; // 10
2806 strConnection = desc.strOvf;
2807
2808 mapNetworks[desc.strOvf] = true;
2809 }
2810 break;
2811
2812 case VirtualSystemDescriptionType_USBController:
2813 /* <Item ovf:required="false">
2814 <rasd:Caption>usb</rasd:Caption>
2815 <rasd:Description>USB Controller</rasd:Description>
2816 <rasd:InstanceId>3</rasd:InstanceId>
2817 <rasd:ResourceType>23</rasd:ResourceType>
2818 <rasd:Address>0</rasd:Address>
2819 <rasd:BusNumber>0</rasd:BusNumber>
2820 </Item> */
2821 if (uLoop == 1)
2822 {
2823 strDescription = "USB Controller";
2824 strCaption = "usb";
2825 type = OVFResourceType_UsbController; // 23
2826 lAddress = 0; // this is what OVFTool writes
2827 lBusNumber = 0; // this is what OVFTool writes
2828 }
2829 break;
2830
2831 case VirtualSystemDescriptionType_SoundCard:
2832 /* <Item ovf:required="false">
2833 <rasd:Caption>sound</rasd:Caption>
2834 <rasd:Description>Sound Card</rasd:Description>
2835 <rasd:InstanceId>10</rasd:InstanceId>
2836 <rasd:ResourceType>35</rasd:ResourceType>
2837 <rasd:ResourceSubType>ensoniq1371</rasd:ResourceSubType>
2838 <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
2839 <rasd:AddressOnParent>3</rasd:AddressOnParent>
2840 </Item> */
2841 if (uLoop == 1)
2842 {
2843 strDescription = "Sound Card";
2844 strCaption = "sound";
2845 type = OVFResourceType_SoundCard; // 35
2846 strResourceSubType = desc.strOvf; // e.g. ensoniq1371
2847 lAutomaticAllocation = 0;
2848 lAddressOnParent = 3; // what gives? this is what OVFTool writes
2849 }
2850 break;
2851 }
2852
2853 if (type)
2854 {
2855 xml::ElementNode *pItem;
2856
2857 pItem = pelmVirtualHardwareSection->createChild("Item");
2858
2859 if (!strDescription.isEmpty())
2860 pItem->createChild("rasd:Description")->addContent(strDescription);
2861 if (!strCaption.isEmpty())
2862 pItem->createChild("rasd:Caption")->addContent(strCaption);
2863
2864 if (!strAllocationUnits.isEmpty())
2865 pItem->createChild("rasd:AllocationUnits")->addContent(strAllocationUnits);
2866
2867 if (lAutomaticAllocation != -1)
2868 pItem->createChild("rasd:AutomaticAllocation")->addContent( (lAutomaticAllocation) ? "true" : "false" );
2869
2870 if (!strConnection.isEmpty())
2871 pItem->createChild("rasd:Connection")->addContent(strConnection);
2872
2873 // <rasd:InstanceID>1</rasd:InstanceID>
2874 pItem->createChild("rasd:InstanceID")->addContent(Utf8StrFmt("%d", ulInstanceID));
2875 ++ulInstanceID;
2876
2877 // <rasd:ResourceType>3</rasd:ResourceType>
2878 pItem->createChild("rasd:ResourceType")->addContent(Utf8StrFmt("%d", type));
2879 if (!strResourceSubType.isEmpty())
2880 pItem->createChild("rasd:ResourceSubType")->addContent(strResourceSubType);
2881
2882 // <rasd:VirtualQuantity>1</rasd:VirtualQuantity>
2883 if (lVirtualQuantity != -1)
2884 pItem->createChild("rasd:VirtualQuantity")->addContent(Utf8StrFmt("%d", lVirtualQuantity));
2885
2886 if (lAddress != -1)
2887 pItem->createChild("rasd:Address")->addContent(Utf8StrFmt("%d", lAddress));
2888
2889 if (lBusNumber != -1)
2890 pItem->createChild("rasd:BusNumber")->addContent(Utf8StrFmt("%d", lBusNumber));
2891
2892 if (ulParent)
2893 pItem->createChild("rasd:Parent")->addContent(Utf8StrFmt("%d", ulParent));
2894 if (lAddressOnParent != -1)
2895 pItem->createChild("rasd:AddressOnParent")->addContent(Utf8StrFmt("%d", lAddressOnParent));
2896
2897 if (!strHostResource.isEmpty())
2898 pItem->createChild("rasd:HostResource")->addContent(strHostResource);
2899 }
2900 }
2901 } // for (size_t uLoop = 0; ...
2902 }
2903
2904 // finally, fill in the network section we set up empty above according
2905 // to the networks we found with the hardware items
2906 map<Utf8Str, bool>::const_iterator itN;
2907 for (itN = mapNetworks.begin();
2908 itN != mapNetworks.end();
2909 ++itN)
2910 {
2911 const Utf8Str &strNetwork = itN->first;
2912 xml::ElementNode *pelmNetwork = pelmNetworkSection->createChild("Network");
2913 pelmNetwork->setAttribute("ovf:name", strNetwork.c_str());
2914 pelmNetwork->createChild("Description")->addContent("Logical network used by this appliance.");
2915 }
2916
2917 map<Utf8Str, const VirtualSystemDescriptionEntry*>::const_iterator itS;
2918 uint32_t ulFile = 1;
2919 for (itS = mapDisks.begin();
2920 itS != mapDisks.end();
2921 ++itS)
2922 {
2923 const Utf8Str &strDiskID = itS->first;
2924 const VirtualSystemDescriptionEntry *pDiskEntry = itS->second;
2925
2926 // source path: where the VBox image is
2927 const Utf8Str &strSrcFilePath = pDiskEntry->strVbox;
2928 Bstr bstrSrcFilePath(strSrcFilePath);
2929 if (!RTPathExists(strSrcFilePath.c_str()))
2930 /* This isn't allowed */
2931 throw setError(VBOX_E_FILE_ERROR,
2932 tr("Source virtual disk image file '%s' doesn't exist"),
2933 strSrcFilePath.c_str());
2934
2935 // output filename
2936 const Utf8Str &strTargetFileNameOnly = pDiskEntry->strOvf;
2937 // target path needs to be composed from where the output OVF is
2938 Utf8Str strTargetFilePath = stripFilename(pAppliance->m->strPath);
2939 strTargetFilePath.append("/");
2940 strTargetFilePath.append(strTargetFileNameOnly);
2941
2942 // clone the disk:
2943 ComPtr<IHardDisk> pSourceDisk;
2944 ComPtr<IHardDisk> pTargetDisk;
2945 ComPtr<IProgress> pProgress2;
2946
2947 Log(("Finding source disk \"%ls\"\n", bstrSrcFilePath.raw()));
2948 rc = pVirtualBox->FindHardDisk(bstrSrcFilePath, pSourceDisk.asOutParam());
2949 if (FAILED(rc)) throw rc;
2950
2951 /* We are always exporting to vmdfk stream optimized for now */
2952 Bstr bstrSrcFormat = L"VMDK";
2953
2954 // create a new hard disk interface for the destination disk image
2955 Log(("Creating target disk \"%s\"\n", strTargetFilePath.raw()));
2956 rc = pVirtualBox->CreateHardDisk(bstrSrcFormat, Bstr(strTargetFilePath), pTargetDisk.asOutParam());
2957 if (FAILED(rc)) throw rc;
2958
2959 // the target disk is now registered and needs to be removed again,
2960 // both after successful cloning or if anything goes bad!
2961 try
2962 {
2963 // clone the source disk image
2964 rc = pSourceDisk->CloneTo(pTargetDisk, HardDiskVariant_VmdkStreamOptimized, pProgress2.asOutParam());
2965 if (FAILED(rc)) throw rc;
2966
2967 // advance to the next operation
2968 if (!task->progress.isNull())
2969 task->progress->advanceOperation(BstrFmt(tr("Exporting virtual disk image '%s'"), strSrcFilePath.c_str()));
2970
2971 // now loop until the asynchronous operation completes and then
2972 // report its result
2973 BOOL fCompleted;
2974 LONG currentPercent;
2975 while (SUCCEEDED(pProgress2->COMGETTER(Completed(&fCompleted))))
2976 {
2977 rc = pProgress2->COMGETTER(Percent(&currentPercent));
2978 if (FAILED(rc)) throw rc;
2979 if (!task->progress.isNull())
2980 task->progress->notifyProgress(currentPercent);
2981 if (fCompleted)
2982 break;
2983 // make sure the loop is not too tight
2984 rc = pProgress2->WaitForCompletion(100);
2985 if (FAILED(rc)) throw rc;
2986 }
2987
2988 // report result of asynchronous operation
2989 HRESULT vrc;
2990 rc = pProgress2->COMGETTER(ResultCode)(&vrc);
2991 if (FAILED(rc)) throw rc;
2992
2993 // if the thread of the progress object has an error, then
2994 // retrieve the error info from there, or it'll be lost
2995 if (FAILED(vrc))
2996 {
2997 ProgressErrorInfo info(pProgress2);
2998 Utf8Str str(info.getText());
2999 const char *pcsz = str.c_str();
3000 HRESULT rc2 = setError(vrc, pcsz);
3001 throw rc2;
3002 }
3003 }
3004 catch (HRESULT rc3)
3005 {
3006 // upon error after registereing, close the disk or
3007 // it'll stick in the registry forever
3008 pTargetDisk->Close();
3009 throw rc3;
3010 }
3011
3012 // we need the following for the XML
3013 uint64_t cbFile = 0; // actual file size
3014 rc = pTargetDisk->COMGETTER(Size)(&cbFile);
3015 if (FAILED(rc)) throw rc;
3016
3017 ULONG64 cbCapacity = 0; // size reported to guest
3018 rc = pTargetDisk->COMGETTER(LogicalSize)(&cbCapacity);
3019 if (FAILED(rc)) throw rc;
3020 // capacity is reported in megabytes, so...
3021 cbCapacity *= _1M;
3022
3023 // upon success, close the disk as well
3024 rc = pTargetDisk->Close();
3025 if (FAILED(rc)) throw rc;
3026
3027 // now handle the XML for the disk:
3028 Utf8StrFmt strFileRef("file%RI32", ulFile++);
3029 // <File ovf:href="WindowsXpProfessional-disk1.vmdk" ovf:id="file1" ovf:size="1710381056"/>
3030 xml::ElementNode *pelmFile = pelmReferences->createChild("File");
3031 pelmFile->setAttribute("ovf:href", strTargetFileNameOnly);
3032 pelmFile->setAttribute("ovf:id", strFileRef);
3033 pelmFile->setAttribute("ovf:size", Utf8StrFmt("%RI64", cbFile).c_str());
3034
3035 // add disk to XML Disks section
3036 // <Disk ovf:capacity="8589934592" ovf:diskId="vmdisk1" ovf:fileRef="file1" ovf:format="http://www.vmware.com/specifications/vmdk.html#sparse"/>
3037 xml::ElementNode *pelmDisk = pelmDiskSection->createChild("Disk");
3038 pelmDisk->setAttribute("ovf:capacity", Utf8StrFmt("%RI64", cbCapacity).c_str());
3039 pelmDisk->setAttribute("ovf:diskId", strDiskID);
3040 pelmDisk->setAttribute("ovf:fileRef", strFileRef);
3041 pelmDisk->setAttribute("ovf:format", "http://www.vmware.com/specifications/vmdk.html#compressed");
3042 }
3043
3044 // now go write the XML
3045 xml::XmlFileWriter writer(doc);
3046 writer.write(pAppliance->m->strPath.c_str());
3047 }
3048 catch(xml::Error &x)
3049 {
3050 rc = setError(VBOX_E_FILE_ERROR,
3051 x.what());
3052 }
3053 catch(HRESULT aRC)
3054 {
3055 rc = aRC;
3056 }
3057
3058 task->rc = rc;
3059
3060 if (!task->progress.isNull())
3061 task->progress->notifyComplete(rc);
3062
3063 LogFlowFunc(("rc=%Rhrc\n", rc));
3064 LogFlowFuncLeave();
3065
3066 return VINF_SUCCESS;
3067}
3068
3069////////////////////////////////////////////////////////////////////////////////
3070//
3071// IVirtualSystemDescription constructor / destructor
3072//
3073////////////////////////////////////////////////////////////////////////////////
3074
3075DEFINE_EMPTY_CTOR_DTOR(VirtualSystemDescription)
3076struct shutup3 {};
3077
3078/**
3079 * COM initializer.
3080 * @return
3081 */
3082HRESULT VirtualSystemDescription::init()
3083{
3084 /* Enclose the state transition NotReady->InInit->Ready */
3085 AutoInitSpan autoInitSpan(this);
3086 AssertReturn(autoInitSpan.isOk(), E_FAIL);
3087
3088 /* Initialize data */
3089 m = new Data();
3090
3091 /* Confirm a successful initialization */
3092 autoInitSpan.setSucceeded();
3093 return S_OK;
3094}
3095
3096/**
3097* COM uninitializer.
3098*/
3099
3100void VirtualSystemDescription::uninit()
3101{
3102 delete m;
3103 m = NULL;
3104}
3105
3106////////////////////////////////////////////////////////////////////////////////
3107//
3108// IVirtualSystemDescription public methods
3109//
3110////////////////////////////////////////////////////////////////////////////////
3111
3112/**
3113 * Public method implementation.
3114 * @param
3115 * @return
3116 */
3117STDMETHODIMP VirtualSystemDescription::COMGETTER(Count)(ULONG *aCount)
3118{
3119 if (!aCount)
3120 return E_POINTER;
3121
3122 AutoCaller autoCaller(this);
3123 CheckComRCReturnRC(autoCaller.rc());
3124
3125 AutoReadLock alock(this);
3126
3127 *aCount = (ULONG)m->llDescriptions.size();
3128
3129 return S_OK;
3130}
3131
3132/**
3133 * Public method implementation.
3134 * @return
3135 */
3136STDMETHODIMP VirtualSystemDescription::GetDescription(ComSafeArrayOut(VirtualSystemDescriptionType_T, aTypes),
3137 ComSafeArrayOut(BSTR, aRefs),
3138 ComSafeArrayOut(BSTR, aOrigValues),
3139 ComSafeArrayOut(BSTR, aVboxValues),
3140 ComSafeArrayOut(BSTR, aExtraConfigValues))
3141{
3142 if (ComSafeArrayOutIsNull(aTypes) ||
3143 ComSafeArrayOutIsNull(aRefs) ||
3144 ComSafeArrayOutIsNull(aOrigValues) ||
3145 ComSafeArrayOutIsNull(aVboxValues) ||
3146 ComSafeArrayOutIsNull(aExtraConfigValues))
3147 return E_POINTER;
3148
3149 AutoCaller autoCaller(this);
3150 CheckComRCReturnRC(autoCaller.rc());
3151
3152 AutoReadLock alock(this);
3153
3154 ULONG c = (ULONG)m->llDescriptions.size();
3155 com::SafeArray<VirtualSystemDescriptionType_T> sfaTypes(c);
3156 com::SafeArray<BSTR> sfaRefs(c);
3157 com::SafeArray<BSTR> sfaOrigValues(c);
3158 com::SafeArray<BSTR> sfaVboxValues(c);
3159 com::SafeArray<BSTR> sfaExtraConfigValues(c);
3160
3161 list<VirtualSystemDescriptionEntry>::const_iterator it;
3162 size_t i = 0;
3163 for (it = m->llDescriptions.begin();
3164 it != m->llDescriptions.end();
3165 ++it, ++i)
3166 {
3167 const VirtualSystemDescriptionEntry &vsde = (*it);
3168
3169 sfaTypes[i] = vsde.type;
3170
3171 Bstr bstr = vsde.strRef;
3172 bstr.cloneTo(&sfaRefs[i]);
3173
3174 bstr = vsde.strOvf;
3175 bstr.cloneTo(&sfaOrigValues[i]);
3176
3177 bstr = vsde.strVbox;
3178 bstr.cloneTo(&sfaVboxValues[i]);
3179
3180 bstr = vsde.strExtraConfig;
3181 bstr.cloneTo(&sfaExtraConfigValues[i]);
3182 }
3183
3184 sfaTypes.detachTo(ComSafeArrayOutArg(aTypes));
3185 sfaRefs.detachTo(ComSafeArrayOutArg(aRefs));
3186 sfaOrigValues.detachTo(ComSafeArrayOutArg(aOrigValues));
3187 sfaVboxValues.detachTo(ComSafeArrayOutArg(aVboxValues));
3188 sfaExtraConfigValues.detachTo(ComSafeArrayOutArg(aExtraConfigValues));
3189
3190 return S_OK;
3191}
3192
3193/**
3194 * Public method implementation.
3195 * @return
3196 */
3197STDMETHODIMP VirtualSystemDescription::SetFinalValues(ComSafeArrayIn(BOOL, aEnabled),
3198 ComSafeArrayIn(IN_BSTR, argVboxValues),
3199 ComSafeArrayIn(IN_BSTR, argExtraConfigValues))
3200{
3201 CheckComArgSafeArrayNotNull(argVboxValues);
3202 CheckComArgSafeArrayNotNull(argExtraConfigValues);
3203
3204 AutoCaller autoCaller(this);
3205 CheckComRCReturnRC(autoCaller.rc());
3206
3207 AutoWriteLock alock(this);
3208
3209 com::SafeArray<IN_BSTR> aVboxValues(ComSafeArrayInArg(argVboxValues));
3210 com::SafeArray<IN_BSTR> aExtraConfigValues(ComSafeArrayInArg(argExtraConfigValues));
3211
3212 if ( (aVboxValues.size() != m->llDescriptions.size())
3213 || (aExtraConfigValues.size() != m->llDescriptions.size())
3214 )
3215 return E_INVALIDARG;
3216
3217 list<VirtualSystemDescriptionEntry>::iterator it;
3218 size_t i = 0;
3219 for (it = m->llDescriptions.begin();
3220 it != m->llDescriptions.end();
3221 ++it, ++i)
3222 {
3223 VirtualSystemDescriptionEntry& vsde = *it;
3224
3225 if (aEnabled[i])
3226 {
3227 vsde.strVbox = aVboxValues[i];
3228 vsde.strExtraConfig = aExtraConfigValues[i];
3229 }
3230 else
3231 vsde.type = VirtualSystemDescriptionType_Ignore;
3232 }
3233
3234 return S_OK;
3235}
3236
3237/**
3238 * Internal method; adds a new description item to the member list.
3239 * @param aType Type of description for the new item.
3240 * @param strRef Reference item; only used with hard disk controllers.
3241 * @param aOrigValue Corresponding original value from OVF.
3242 * @param aAutoValue Initial configuration value (can be overridden by caller with setFinalValues).
3243 * @param strExtraConfig Extra configuration; meaning dependent on type.
3244 */
3245void VirtualSystemDescription::addEntry(VirtualSystemDescriptionType_T aType,
3246 const Utf8Str &strRef,
3247 const Utf8Str &aOrigValue,
3248 const Utf8Str &aAutoValue,
3249 const Utf8Str &strExtraConfig /*= ""*/)
3250{
3251 VirtualSystemDescriptionEntry vsde;
3252 vsde.ulIndex = (uint32_t)m->llDescriptions.size(); // each entry gets an index so the client side can reference them
3253 vsde.type = aType;
3254 vsde.strRef = strRef;
3255 vsde.strOvf = aOrigValue;
3256 vsde.strVbox = aAutoValue;
3257 vsde.strExtraConfig = strExtraConfig;
3258
3259 m->llDescriptions.push_back(vsde);
3260}
3261
3262/**
3263 * Private method; returns a list of description items containing all the items from the member
3264 * description items of this virtual system that match the given type.
3265 * @param aType
3266 * @return
3267 */
3268std::list<VirtualSystemDescriptionEntry*> VirtualSystemDescription::findByType(VirtualSystemDescriptionType_T aType)
3269{
3270 std::list<VirtualSystemDescriptionEntry*> vsd;
3271
3272 list<VirtualSystemDescriptionEntry>::iterator it;
3273 for (it = m->llDescriptions.begin();
3274 it != m->llDescriptions.end();
3275 ++it)
3276 {
3277 if (it->type == aType)
3278 vsd.push_back(&(*it));
3279 }
3280
3281 return vsd;
3282}
3283
3284/**
3285 * Private method; looks thru the member hardware items for the IDE, SATA, or SCSI controller with
3286 * the given reference ID. Useful when needing the controller for a particular
3287 * virtual disk.
3288 * @param id
3289 * @return
3290 */
3291const VirtualSystemDescriptionEntry* VirtualSystemDescription::findControllerFromID(uint32_t id)
3292{
3293 Utf8Str strRef = Utf8StrFmt("%RI32", id);
3294 list<VirtualSystemDescriptionEntry>::const_iterator it;
3295 for (it = m->llDescriptions.begin();
3296 it != m->llDescriptions.end();
3297 ++it)
3298 {
3299 const VirtualSystemDescriptionEntry &d = *it;
3300 switch (d.type)
3301 {
3302 case VirtualSystemDescriptionType_HardDiskControllerIDE:
3303 case VirtualSystemDescriptionType_HardDiskControllerSATA:
3304 case VirtualSystemDescriptionType_HardDiskControllerSCSI:
3305 if (d.strRef == strRef)
3306 return &d;
3307 break;
3308 }
3309 }
3310
3311 return NULL;
3312}
3313
3314////////////////////////////////////////////////////////////////////////////////
3315//
3316// IMachine public methods
3317//
3318////////////////////////////////////////////////////////////////////////////////
3319
3320// This code is here so we won't have to include the appliance headers in the
3321// IMachine implementation, and we also need to access private appliance data.
3322
3323/**
3324* Public method implementation.
3325* @param appliance
3326* @return
3327*/
3328
3329STDMETHODIMP Machine::Export(IAppliance *appliance)
3330{
3331 HRESULT rc = S_OK;
3332
3333 if (!appliance)
3334 return E_POINTER;
3335
3336 AutoCaller autoCaller(this);
3337 CheckComRCReturnRC(autoCaller.rc());
3338
3339 AutoReadLock alock(this);
3340
3341 ComObjPtr<VirtualSystemDescription> pNewDesc;
3342
3343 try
3344 {
3345 Bstr bstrName;
3346 Bstr bstrDescription;
3347 Bstr bstrGuestOSType;
3348 uint32_t cCPUs;
3349 uint32_t ulMemSizeMB;
3350 BOOL fDVDEnabled;
3351 BOOL fFloppyEnabled;
3352 BOOL fUSBEnabled;
3353 BOOL fAudioEnabled;
3354 AudioControllerType_T audioController;
3355
3356 ComPtr<IUSBController> pUsbController;
3357 ComPtr<IAudioAdapter> pAudioAdapter;
3358
3359 // get name
3360 bstrName = mUserData->mName;
3361 // get description
3362 bstrDescription = mUserData->mDescription;
3363 // get guest OS
3364 bstrGuestOSType = mUserData->mOSTypeId;
3365 // CPU count
3366 cCPUs = mHWData->mCPUCount;
3367 // memory size in MB
3368 ulMemSizeMB = mHWData->mMemorySize;
3369 // VRAM size?
3370 // BIOS settings?
3371 // 3D acceleration enabled?
3372 // hardware virtualization enabled?
3373 // nested paging enabled?
3374 // HWVirtExVPIDEnabled?
3375 // PAEEnabled?
3376 // snapshotFolder?
3377 // VRDPServer?
3378
3379 // floppy
3380 rc = mFloppyDrive->COMGETTER(Enabled)(&fFloppyEnabled);
3381 if (FAILED(rc)) throw rc;
3382
3383 // CD-ROM ?!?
3384 // ComPtr<IDVDDrive> pDVDDrive;
3385 fDVDEnabled = 1;
3386
3387 // this is more tricky so use the COM method
3388 rc = COMGETTER(USBController)(pUsbController.asOutParam());
3389 if (FAILED(rc)) throw rc;
3390 rc = pUsbController->COMGETTER(Enabled)(&fUSBEnabled);
3391
3392 pAudioAdapter = mAudioAdapter;
3393 rc = pAudioAdapter->COMGETTER(Enabled)(&fAudioEnabled);
3394 if (FAILED(rc)) throw rc;
3395 rc = pAudioAdapter->COMGETTER(AudioController)(&audioController);
3396 if (FAILED(rc)) throw rc;
3397
3398 // create a new virtual system
3399 rc = pNewDesc.createObject();
3400 CheckComRCThrowRC(rc);
3401 rc = pNewDesc->init();
3402 CheckComRCThrowRC(rc);
3403
3404 /* Guest OS type */
3405 Utf8Str strOsTypeVBox(bstrGuestOSType);
3406 CIMOSType_T cim = convertVBoxOSType2CIMOSType(strOsTypeVBox.c_str());
3407 pNewDesc->addEntry(VirtualSystemDescriptionType_OS,
3408 "",
3409 Utf8StrFmt("%RI32", cim),
3410 strOsTypeVBox);
3411
3412 /* VM name */
3413 Utf8Str strVMName(bstrName);
3414 pNewDesc->addEntry(VirtualSystemDescriptionType_Name,
3415 "",
3416 strVMName,
3417 strVMName);
3418
3419 /* CPU count*/
3420 Utf8Str strCpuCount = Utf8StrFmt("%RI32", cCPUs);
3421 pNewDesc->addEntry(VirtualSystemDescriptionType_CPU,
3422 "",
3423 strCpuCount,
3424 strCpuCount);
3425
3426 /* Memory */
3427 Utf8Str strMemory = Utf8StrFmt("%RI32", (uint64_t)ulMemSizeMB * _1M);
3428 pNewDesc->addEntry(VirtualSystemDescriptionType_Memory,
3429 "",
3430 strMemory,
3431 strMemory);
3432
3433 int32_t lIDEControllerIndex = 0;
3434 int32_t lSATAControllerIndex = 0;
3435 int32_t lSCSIControllerIndex = 0;
3436
3437// <const name="HardDiskControllerIDE" value="6" />
3438 ComPtr<IStorageController> pController;
3439 rc = GetStorageControllerByName(Bstr("IDE"), pController.asOutParam());
3440 if (FAILED(rc)) throw rc;
3441 Utf8Str strVbox;
3442 StorageControllerType_T ctlr;
3443 rc = pController->COMGETTER(ControllerType)(&ctlr);
3444 if (FAILED(rc)) throw rc;
3445 switch(ctlr)
3446 {
3447 case StorageControllerType_PIIX3: strVbox = "PIIX3"; break;
3448 case StorageControllerType_PIIX4: strVbox = "PIIX4"; break;
3449 case StorageControllerType_ICH6: strVbox = "ICH6"; break;
3450 }
3451
3452 if (strVbox.length())
3453 {
3454 lIDEControllerIndex = (int32_t)pNewDesc->m->llDescriptions.size();
3455 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerIDE,
3456 Utf8StrFmt("%d", lIDEControllerIndex),
3457 strVbox,
3458 strVbox);
3459 }
3460
3461#ifdef VBOX_WITH_AHCI
3462// <const name="HardDiskControllerSATA" value="7" />
3463 rc = GetStorageControllerByName(Bstr("SATA"), pController.asOutParam());
3464 if (SUCCEEDED(rc))
3465 {
3466 strVbox = "AHCI";
3467 lSATAControllerIndex = (int32_t)pNewDesc->m->llDescriptions.size();
3468 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSATA,
3469 Utf8StrFmt("%d", lSATAControllerIndex),
3470 strVbox,
3471 strVbox);
3472 }
3473#endif // VBOX_WITH_AHCI
3474
3475// <const name="HardDiskControllerSCSI" value="8" />
3476 rc = GetStorageControllerByName(Bstr("SCSI"), pController.asOutParam());
3477 if (SUCCEEDED(rc))
3478 {
3479 rc = pController->COMGETTER(ControllerType)(&ctlr);
3480 if (SUCCEEDED(rc))
3481 {
3482 strVbox = "LsiLogic"; // the default in VBox
3483 switch(ctlr)
3484 {
3485 case StorageControllerType_LsiLogic: strVbox = "LsiLogic"; break;
3486 case StorageControllerType_BusLogic: strVbox = "BusLogic"; break;
3487 }
3488 lSCSIControllerIndex = (int32_t)pNewDesc->m->llDescriptions.size();
3489 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSCSI,
3490 Utf8StrFmt("%d", lSCSIControllerIndex),
3491 strVbox,
3492 strVbox);
3493 }
3494 else
3495 throw rc;
3496 }
3497
3498// <const name="HardDiskImage" value="9" />
3499 HDData::AttachmentList::iterator itA;
3500 for (itA = mHDData->mAttachments.begin();
3501 itA != mHDData->mAttachments.end();
3502 ++itA)
3503 {
3504 ComObjPtr<HardDiskAttachment> pHDA = *itA;
3505
3506 // the attachment's data
3507 ComPtr<IHardDisk> pHardDisk;
3508 ComPtr<IStorageController> ctl;
3509 Bstr controllerName;
3510
3511 rc = pHDA->COMGETTER(Controller)(controllerName.asOutParam());
3512 if (FAILED(rc)) throw rc;
3513
3514 rc = GetStorageControllerByName(controllerName, ctl.asOutParam());
3515 if (FAILED(rc)) throw rc;
3516
3517 StorageBus_T storageBus;
3518 LONG lChannel;
3519 LONG lDevice;
3520
3521 rc = ctl->COMGETTER(Bus)(&storageBus);
3522 if (FAILED(rc)) throw rc;
3523
3524 rc = pHDA->COMGETTER(HardDisk)(pHardDisk.asOutParam());
3525 if (FAILED(rc)) throw rc;
3526
3527 rc = pHDA->COMGETTER(Port)(&lChannel);
3528 if (FAILED(rc)) throw rc;
3529
3530 rc = pHDA->COMGETTER(Device)(&lDevice);
3531 if (FAILED(rc)) throw rc;
3532
3533 Bstr bstrLocation;
3534 rc = pHardDisk->COMGETTER(Location)(bstrLocation.asOutParam());
3535 Bstr bstrName;
3536 rc = pHardDisk->COMGETTER(Name)(bstrName.asOutParam());
3537
3538 // and how this translates to the virtual system
3539 int32_t lControllerVsys = 0;
3540 LONG lChannelVsys;
3541
3542 switch (storageBus)
3543 {
3544 case StorageBus_IDE:
3545 // this is the exact reverse to what we're doing in Appliance::taskThreadImportMachines,
3546 // and it must be updated when that is changed!
3547
3548 if (lChannel == 0 && lDevice == 0) // primary master
3549 lChannelVsys = 0;
3550 else if (lChannel == 0 && lDevice == 1) // primary slave
3551 lChannelVsys = 1;
3552 else if (lChannel == 1 && lDevice == 1) // secondary slave; secondary master is always CDROM
3553 lChannelVsys = 2;
3554 else
3555 throw setError(VBOX_E_NOT_SUPPORTED,
3556 tr("Cannot handle hard disk attachment: channel is %d, device is %d"), lChannel, lDevice);
3557
3558 lControllerVsys = lIDEControllerIndex;
3559 break;
3560
3561 case StorageBus_SATA:
3562 lChannelVsys = lChannel; // should be between 0 and 29
3563 lControllerVsys = lSATAControllerIndex;
3564 break;
3565
3566 case StorageBus_SCSI:
3567 lChannelVsys = lChannel; // should be between 0 and 15
3568 lControllerVsys = lSCSIControllerIndex;
3569 break;
3570
3571 default:
3572 throw setError(VBOX_E_NOT_SUPPORTED,
3573 tr("Cannot handle hard disk attachment: storageBus is %d, channel is %d, device is %d"), storageBus, lChannel, lDevice);
3574 break;
3575 }
3576
3577 Utf8Str strTargetVmdkName(bstrName);
3578 RTPathStripExt(strTargetVmdkName.mutableRaw());
3579 strTargetVmdkName.append(".vmdk");
3580
3581 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskImage,
3582 strTargetVmdkName, // disk ID: let's use the name
3583 strTargetVmdkName, // OVF value:
3584 Utf8Str(bstrLocation), // vbox value: media path
3585 Utf8StrFmt("controller=%RI32;channel=%RI32", lControllerVsys, lChannelVsys));
3586 }
3587
3588 /* Floppy Drive */
3589 if (fFloppyEnabled)
3590 pNewDesc->addEntry(VirtualSystemDescriptionType_Floppy, "", "", "");
3591
3592 /* CD Drive */
3593 if (fDVDEnabled)
3594 pNewDesc->addEntry(VirtualSystemDescriptionType_CDROM, "", "", "");
3595
3596// <const name="NetworkAdapter" />
3597 size_t a;
3598 for (a = 0;
3599 a < SchemaDefs::NetworkAdapterCount;
3600 ++a)
3601 {
3602 ComPtr<INetworkAdapter> pNetworkAdapter;
3603 BOOL fEnabled;
3604 NetworkAdapterType_T adapterType;
3605 NetworkAttachmentType_T attachmentType;
3606
3607 rc = GetNetworkAdapter((ULONG)a, pNetworkAdapter.asOutParam());
3608 if (FAILED(rc)) throw rc;
3609 /* Enable the network card & set the adapter type */
3610 rc = pNetworkAdapter->COMGETTER(Enabled)(&fEnabled);
3611 if (FAILED(rc)) throw rc;
3612
3613 if (fEnabled)
3614 {
3615 Utf8Str strAttachmentType;
3616
3617 rc = pNetworkAdapter->COMGETTER(AdapterType)(&adapterType);
3618 if (FAILED(rc)) throw rc;
3619
3620 rc = pNetworkAdapter->COMGETTER(AttachmentType)(&attachmentType);
3621 if (FAILED(rc)) throw rc;
3622
3623 switch (attachmentType)
3624 {
3625 case NetworkAttachmentType_Null:
3626 strAttachmentType = "Null";
3627 break;
3628
3629 case NetworkAttachmentType_NAT:
3630 strAttachmentType = "NAT";
3631 break;
3632
3633 case NetworkAttachmentType_Bridged:
3634 strAttachmentType = "Bridged";
3635 break;
3636
3637 case NetworkAttachmentType_Internal:
3638 strAttachmentType = "Internal";
3639 break;
3640
3641 case NetworkAttachmentType_HostOnly:
3642 strAttachmentType = "HostOnly";
3643 break;
3644 }
3645
3646 pNewDesc->addEntry(VirtualSystemDescriptionType_NetworkAdapter,
3647 "", // ref
3648 strAttachmentType, // orig
3649 Utf8StrFmt("%RI32", (uint32_t)adapterType), // conf
3650 Utf8StrFmt("type=%s", strAttachmentType.c_str())); // extra conf
3651 }
3652 }
3653
3654// <const name="USBController" />
3655#ifdef VBOX_WITH_USB
3656 if (fUSBEnabled)
3657 pNewDesc->addEntry(VirtualSystemDescriptionType_USBController, "", "", "");
3658#endif /* VBOX_WITH_USB */
3659
3660// <const name="SoundCard" />
3661 if (fAudioEnabled)
3662 {
3663 pNewDesc->addEntry(VirtualSystemDescriptionType_SoundCard,
3664 "",
3665 "ensoniq1371", // this is what OVFTool writes and VMware supports
3666 Utf8StrFmt("%RI32", audioController));
3667 }
3668
3669 // finally, add the virtual system to the appliance
3670 Appliance *pAppliance = static_cast<Appliance*>(appliance);
3671 AutoCaller autoCaller(pAppliance);
3672 if (FAILED(rc)) throw rc;
3673
3674 AutoWriteLock alock(pAppliance);
3675
3676 pAppliance->m->virtualSystemDescriptions.push_back(pNewDesc);
3677 }
3678 catch(HRESULT arc)
3679 {
3680 rc = arc;
3681 }
3682
3683 return rc;
3684}
3685
3686/* vi: set tabstop=4 shiftwidth=4 expandtab: */
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