VirtualBox

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

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

OVF: added manifest file creation/verification

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 189.4 KB
Line 
1/* $Id: ApplianceImpl.cpp 21758 2009-07-22 09:15:32Z 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#include <iprt/s3.h>
28#include <iprt/sha1.h>
29#include <iprt/manifest.h>
30
31#include "ovfreader.h"
32
33#include <VBox/param.h>
34#include <VBox/version.h>
35
36#include "ApplianceImpl.h"
37#include "VFSExplorerImpl.h"
38#include "VirtualBoxImpl.h"
39#include "GuestOSTypeImpl.h"
40#include "ProgressImpl.h"
41#include "MachineImpl.h"
42#include "HostNetworkInterfaceImpl.h"
43
44#include "Logging.h"
45
46using namespace std;
47
48////////////////////////////////////////////////////////////////////////////////
49//
50// Appliance data definition
51//
52////////////////////////////////////////////////////////////////////////////////
53
54/* Describe a location for the import/export. The location could be a file on a
55 * local hard disk or a remote target based on the supported inet protocols. */
56struct Appliance::LocationInfo
57{
58 LocationInfo()
59 : storageType(VFSType_File) {}
60 VFSType_T storageType; /* Which type of storage should be handled */
61 Utf8Str strPath; /* File path for the import/export */
62 Utf8Str strHostname; /* Hostname on remote storage locations (could be empty) */
63 Utf8Str strUsername; /* Username on remote storage locations (could be empty) */
64 Utf8Str strPassword; /* Password on remote storage locations (could be empty) */
65};
66
67// opaque private instance data of Appliance class
68struct Appliance::Data
69{
70 Data()
71 : pReader(NULL) {}
72
73 ~Data()
74 {
75 if (pReader)
76 {
77 delete pReader;
78 pReader = NULL;
79 }
80 }
81
82 LocationInfo locInfo; /* The location info for the currently processed OVF */
83
84 OVFReader *pReader;
85
86 list< ComObjPtr<VirtualSystemDescription> > virtualSystemDescriptions;
87
88 list<Utf8Str> llWarnings;
89
90 ULONG ulWeightPerOperation;
91 Utf8Str strOVFSHA1Digest;
92};
93
94struct VirtualSystemDescription::Data
95{
96 list<VirtualSystemDescriptionEntry> llDescriptions;
97};
98
99////////////////////////////////////////////////////////////////////////////////
100//
101// internal helpers
102//
103////////////////////////////////////////////////////////////////////////////////
104
105static const struct
106{
107 CIMOSType_T cim;
108 const char *pcszVbox;
109}
110g_osTypes[] =
111{
112 { CIMOSType_CIMOS_Unknown, SchemaDefs_OSTypeId_Other },
113 { CIMOSType_CIMOS_OS2, SchemaDefs_OSTypeId_OS2 },
114 { CIMOSType_CIMOS_MSDOS, SchemaDefs_OSTypeId_DOS },
115 { CIMOSType_CIMOS_WIN3x, SchemaDefs_OSTypeId_Windows31 },
116 { CIMOSType_CIMOS_WIN95, SchemaDefs_OSTypeId_Windows95 },
117 { CIMOSType_CIMOS_WIN98, SchemaDefs_OSTypeId_Windows98 },
118 { CIMOSType_CIMOS_WINNT, SchemaDefs_OSTypeId_WindowsNT4 },
119 { CIMOSType_CIMOS_NetWare, SchemaDefs_OSTypeId_Netware },
120 { CIMOSType_CIMOS_NovellOES, SchemaDefs_OSTypeId_Netware },
121 { CIMOSType_CIMOS_Solaris, SchemaDefs_OSTypeId_OpenSolaris },
122 { CIMOSType_CIMOS_SunOS, SchemaDefs_OSTypeId_OpenSolaris },
123 { CIMOSType_CIMOS_FreeBSD, SchemaDefs_OSTypeId_FreeBSD },
124 { CIMOSType_CIMOS_NetBSD, SchemaDefs_OSTypeId_NetBSD },
125 { CIMOSType_CIMOS_QNX, SchemaDefs_OSTypeId_QNX },
126 { CIMOSType_CIMOS_Windows2000, SchemaDefs_OSTypeId_Windows2000 },
127 { CIMOSType_CIMOS_WindowsMe, SchemaDefs_OSTypeId_WindowsMe },
128 { CIMOSType_CIMOS_OpenBSD, SchemaDefs_OSTypeId_OpenBSD },
129 { CIMOSType_CIMOS_WindowsXP, SchemaDefs_OSTypeId_WindowsXP },
130 { CIMOSType_CIMOS_WindowsXPEmbedded, SchemaDefs_OSTypeId_WindowsXP },
131 { CIMOSType_CIMOS_WindowsEmbeddedforPointofService, SchemaDefs_OSTypeId_WindowsXP },
132 { CIMOSType_CIMOS_MicrosoftWindowsServer2003, SchemaDefs_OSTypeId_Windows2003 },
133 { CIMOSType_CIMOS_MicrosoftWindowsServer2003_64, SchemaDefs_OSTypeId_Windows2003_64 },
134 { CIMOSType_CIMOS_WindowsXP_64, SchemaDefs_OSTypeId_WindowsXP_64 },
135 { CIMOSType_CIMOS_WindowsVista, SchemaDefs_OSTypeId_WindowsVista },
136 { CIMOSType_CIMOS_WindowsVista_64, SchemaDefs_OSTypeId_WindowsVista_64 },
137 { CIMOSType_CIMOS_MicrosoftWindowsServer2008, SchemaDefs_OSTypeId_Windows2008 },
138 { CIMOSType_CIMOS_MicrosoftWindowsServer2008_64, SchemaDefs_OSTypeId_Windows2008_64 },
139 { CIMOSType_CIMOS_FreeBSD_64, SchemaDefs_OSTypeId_FreeBSD_64 },
140 { CIMOSType_CIMOS_RedHatEnterpriseLinux, SchemaDefs_OSTypeId_RedHat },
141 { CIMOSType_CIMOS_RedHatEnterpriseLinux_64, SchemaDefs_OSTypeId_RedHat_64 },
142 { CIMOSType_CIMOS_Solaris_64, SchemaDefs_OSTypeId_OpenSolaris_64 },
143 { CIMOSType_CIMOS_SUSE, SchemaDefs_OSTypeId_OpenSUSE },
144 { CIMOSType_CIMOS_SLES, SchemaDefs_OSTypeId_OpenSUSE },
145 { CIMOSType_CIMOS_NovellLinuxDesktop, SchemaDefs_OSTypeId_OpenSUSE },
146 { CIMOSType_CIMOS_SUSE_64, SchemaDefs_OSTypeId_OpenSUSE_64 },
147 { CIMOSType_CIMOS_SLES_64, SchemaDefs_OSTypeId_OpenSUSE_64 },
148 { CIMOSType_CIMOS_LINUX, SchemaDefs_OSTypeId_Linux },
149 { CIMOSType_CIMOS_SunJavaDesktopSystem, SchemaDefs_OSTypeId_Linux },
150 { CIMOSType_CIMOS_TurboLinux, SchemaDefs_OSTypeId_Linux},
151
152 // { CIMOSType_CIMOS_TurboLinux_64, },
153
154 { CIMOSType_CIMOS_Mandriva, SchemaDefs_OSTypeId_Mandriva },
155 { CIMOSType_CIMOS_Mandriva_64, SchemaDefs_OSTypeId_Mandriva_64 },
156 { CIMOSType_CIMOS_Ubuntu, SchemaDefs_OSTypeId_Ubuntu },
157 { CIMOSType_CIMOS_Ubuntu_64, SchemaDefs_OSTypeId_Ubuntu_64 },
158 { CIMOSType_CIMOS_Debian, SchemaDefs_OSTypeId_Debian },
159 { CIMOSType_CIMOS_Debian_64, SchemaDefs_OSTypeId_Debian_64 },
160 { CIMOSType_CIMOS_Linux_2_4_x, SchemaDefs_OSTypeId_Linux24 },
161 { CIMOSType_CIMOS_Linux_2_4_x_64, SchemaDefs_OSTypeId_Linux24_64 },
162 { CIMOSType_CIMOS_Linux_2_6_x, SchemaDefs_OSTypeId_Linux26 },
163 { CIMOSType_CIMOS_Linux_2_6_x_64, SchemaDefs_OSTypeId_Linux26_64 },
164 { CIMOSType_CIMOS_Linux_64, SchemaDefs_OSTypeId_Linux26_64 }
165};
166
167/* Pattern structure for matching the OS type description field */
168struct osTypePattern
169{
170 const char *pcszPattern;
171 const char *pcszVbox;
172};
173
174/* These are the 32-Bit ones. They are sorted by priority. */
175static const osTypePattern g_osTypesPattern[] =
176{
177 {"Windows NT", SchemaDefs_OSTypeId_WindowsNT4},
178 {"Windows XP", SchemaDefs_OSTypeId_WindowsXP},
179 {"Windows 2000", SchemaDefs_OSTypeId_Windows2000},
180 {"Windows 2003", SchemaDefs_OSTypeId_Windows2003},
181 {"Windows Vista", SchemaDefs_OSTypeId_WindowsVista},
182 {"Windows 2008", SchemaDefs_OSTypeId_Windows2008},
183 {"SUSE", SchemaDefs_OSTypeId_OpenSUSE},
184 {"Novell", SchemaDefs_OSTypeId_OpenSUSE},
185 {"Red Hat", SchemaDefs_OSTypeId_RedHat},
186 {"Mandriva", SchemaDefs_OSTypeId_Mandriva},
187 {"Ubuntu", SchemaDefs_OSTypeId_Ubuntu},
188 {"Debian", SchemaDefs_OSTypeId_Debian},
189 {"QNX", SchemaDefs_OSTypeId_QNX},
190 {"Linux 2.4", SchemaDefs_OSTypeId_Linux24},
191 {"Linux 2.6", SchemaDefs_OSTypeId_Linux26},
192 {"Linux", SchemaDefs_OSTypeId_Linux},
193 {"OpenSolaris", SchemaDefs_OSTypeId_OpenSolaris},
194 {"Solaris", SchemaDefs_OSTypeId_OpenSolaris},
195 {"FreeBSD", SchemaDefs_OSTypeId_FreeBSD},
196 {"NetBSD", SchemaDefs_OSTypeId_NetBSD},
197 {"Windows 95", SchemaDefs_OSTypeId_Windows95},
198 {"Windows 98", SchemaDefs_OSTypeId_Windows98},
199 {"Windows Me", SchemaDefs_OSTypeId_WindowsMe},
200 {"Windows 3.", SchemaDefs_OSTypeId_Windows31},
201 {"DOS", SchemaDefs_OSTypeId_DOS},
202 {"OS2", SchemaDefs_OSTypeId_OS2}
203};
204
205/* These are the 64-Bit ones. They are sorted by priority. */
206static const osTypePattern g_osTypesPattern64[] =
207{
208 {"Windows XP", SchemaDefs_OSTypeId_WindowsXP_64},
209 {"Windows 2003", SchemaDefs_OSTypeId_Windows2003_64},
210 {"Windows Vista", SchemaDefs_OSTypeId_WindowsVista_64},
211 {"Windows 2008", SchemaDefs_OSTypeId_Windows2008_64},
212 {"SUSE", SchemaDefs_OSTypeId_OpenSUSE_64},
213 {"Novell", SchemaDefs_OSTypeId_OpenSUSE_64},
214 {"Red Hat", SchemaDefs_OSTypeId_RedHat_64},
215 {"Mandriva", SchemaDefs_OSTypeId_Mandriva_64},
216 {"Ubuntu", SchemaDefs_OSTypeId_Ubuntu_64},
217 {"Debian", SchemaDefs_OSTypeId_Debian_64},
218 {"Linux 2.4", SchemaDefs_OSTypeId_Linux24_64},
219 {"Linux 2.6", SchemaDefs_OSTypeId_Linux26_64},
220 {"Linux", SchemaDefs_OSTypeId_Linux26_64},
221 {"OpenSolaris", SchemaDefs_OSTypeId_OpenSolaris_64},
222 {"Solaris", SchemaDefs_OSTypeId_OpenSolaris_64},
223 {"FreeBSD", SchemaDefs_OSTypeId_FreeBSD_64},
224};
225
226/**
227 * Private helper func that suggests a VirtualBox guest OS type
228 * for the given OVF operating system type.
229 * @param osTypeVBox
230 * @param c
231 * @param cStr
232 */
233static void convertCIMOSType2VBoxOSType(Utf8Str &strType, CIMOSType_T c, const Utf8Str &cStr)
234{
235 /* First check if the type is other/other_64 */
236 if (c == CIMOSType_CIMOS_Other)
237 {
238 for (size_t i=0; i < RT_ELEMENTS(g_osTypesPattern); ++i)
239 if (cStr.contains (g_osTypesPattern[i].pcszPattern, Utf8Str::CaseInsensitive))
240 {
241 strType = g_osTypesPattern[i].pcszVbox;
242 return;
243 }
244 }
245 else if (c == CIMOSType_CIMOS_Other_64)
246 {
247 for (size_t i=0; i < RT_ELEMENTS(g_osTypesPattern64); ++i)
248 if (cStr.contains (g_osTypesPattern64[i].pcszPattern, Utf8Str::CaseInsensitive))
249 {
250 strType = g_osTypesPattern64[i].pcszVbox;
251 return;
252 }
253 }
254
255 for (size_t i = 0; i < RT_ELEMENTS(g_osTypes); ++i)
256 {
257 if (c == g_osTypes[i].cim)
258 {
259 strType = g_osTypes[i].pcszVbox;
260 return;
261 }
262 }
263
264 strType = SchemaDefs_OSTypeId_Other;
265}
266
267/**
268 * Private helper func that suggests a VirtualBox guest OS type
269 * for the given OVF operating system type.
270 * @param osTypeVBox
271 * @param c
272 */
273static CIMOSType_T convertVBoxOSType2CIMOSType(const char *pcszVbox)
274{
275 for (size_t i = 0; i < RT_ELEMENTS(g_osTypes); ++i)
276 {
277 if (!RTStrICmp(pcszVbox, g_osTypes[i].pcszVbox))
278 return g_osTypes[i].cim;
279 }
280
281 return CIMOSType_CIMOS_Other;
282}
283
284////////////////////////////////////////////////////////////////////////////////
285//
286// IVirtualBox public methods
287//
288////////////////////////////////////////////////////////////////////////////////
289
290// This code is here so we won't have to include the appliance headers in the
291// IVirtualBox implementation.
292
293/**
294 * Implementation for IVirtualBox::createAppliance.
295 *
296 * @param anAppliance IAppliance object created if S_OK is returned.
297 * @return S_OK or error.
298 */
299STDMETHODIMP VirtualBox::CreateAppliance(IAppliance** anAppliance)
300{
301 HRESULT rc;
302
303 ComObjPtr<Appliance> appliance;
304 appliance.createObject();
305 rc = appliance->init(this);
306
307 if (SUCCEEDED(rc))
308 appliance.queryInterfaceTo(anAppliance);
309
310 return rc;
311}
312
313////////////////////////////////////////////////////////////////////////////////
314//
315// Appliance constructor / destructor
316//
317////////////////////////////////////////////////////////////////////////////////
318
319DEFINE_EMPTY_CTOR_DTOR(Appliance)
320
321/**
322 * Appliance COM initializer.
323 * @param
324 * @return
325 */
326HRESULT Appliance::init(VirtualBox *aVirtualBox)
327{
328 /* Enclose the state transition NotReady->InInit->Ready */
329 AutoInitSpan autoInitSpan(this);
330 AssertReturn(autoInitSpan.isOk(), E_FAIL);
331
332 /* Weak reference to a VirtualBox object */
333 unconst(mVirtualBox) = aVirtualBox;
334
335 // initialize data
336 m = new Data;
337
338 /* Confirm a successful initialization */
339 autoInitSpan.setSucceeded();
340
341 return S_OK;
342}
343
344/**
345 * Appliance COM uninitializer.
346 * @return
347 */
348void Appliance::uninit()
349{
350 delete m;
351 m = NULL;
352}
353
354////////////////////////////////////////////////////////////////////////////////
355//
356// Appliance private methods
357//
358////////////////////////////////////////////////////////////////////////////////
359
360HRESULT Appliance::searchUniqueVMName(Utf8Str& aName) const
361{
362 IMachine *machine = NULL;
363 char *tmpName = RTStrDup(aName.c_str());
364 int i = 1;
365 /* @todo: Maybe too cost-intensive; try to find a lighter way */
366 while (mVirtualBox->FindMachine(Bstr(tmpName), &machine) != VBOX_E_OBJECT_NOT_FOUND)
367 {
368 RTStrFree(tmpName);
369 RTStrAPrintf(&tmpName, "%s_%d", aName.c_str(), i);
370 ++i;
371 }
372 aName = tmpName;
373 RTStrFree(tmpName);
374
375 return S_OK;
376}
377
378HRESULT Appliance::searchUniqueDiskImageFilePath(Utf8Str& aName) const
379{
380 IHardDisk *harddisk = NULL;
381 char *tmpName = RTStrDup(aName.c_str());
382 int i = 1;
383 /* Check if the file exists or if a file with this path is registered
384 * already */
385 /* @todo: Maybe too cost-intensive; try to find a lighter way */
386 while (RTPathExists(tmpName) ||
387 mVirtualBox->FindHardDisk(Bstr(tmpName), &harddisk) != VBOX_E_OBJECT_NOT_FOUND)
388 {
389 RTStrFree(tmpName);
390 char *tmpDir = RTStrDup(aName.c_str());
391 RTPathStripFilename(tmpDir);;
392 char *tmpFile = RTStrDup(RTPathFilename(aName.c_str()));
393 RTPathStripExt(tmpFile);
394 const char *tmpExt = RTPathExt(aName.c_str());
395 RTStrAPrintf(&tmpName, "%s%c%s_%d%s", tmpDir, RTPATH_DELIMITER, tmpFile, i, tmpExt);
396 RTStrFree(tmpFile);
397 RTStrFree(tmpDir);
398 ++i;
399 }
400 aName = tmpName;
401 RTStrFree(tmpName);
402
403 return S_OK;
404}
405
406/**
407 * Called from the import and export background threads to synchronize the second
408 * background disk thread's progress object with the current progress object so
409 * that the user interface sees progress correctly and that cancel signals are
410 * passed on to the second thread.
411 * @param pProgressThis Progress object of the current thread.
412 * @param pProgressAsync Progress object of asynchronous task running in background.
413 */
414void Appliance::waitForAsyncProgress(ComObjPtr<Progress> &pProgressThis,
415 ComPtr<IProgress> &pProgressAsync)
416{
417 HRESULT rc;
418
419 // now loop until the asynchronous operation completes and then report its result
420 BOOL fCompleted;
421 BOOL fCanceled;
422 ULONG currentPercent;
423 while (SUCCEEDED(pProgressAsync->COMGETTER(Completed(&fCompleted))))
424 {
425 rc = pProgressThis->COMGETTER(Canceled)(&fCanceled);
426 if (FAILED(rc)) throw rc;
427 if (fCanceled)
428 {
429 pProgressAsync->Cancel();
430 break;
431 }
432
433 rc = pProgressAsync->COMGETTER(Percent(&currentPercent));
434 if (FAILED(rc)) throw rc;
435 if (!pProgressThis.isNull())
436 pProgressThis->setCurrentOperationProgress(currentPercent);
437 if (fCompleted)
438 break;
439
440 /* Make sure the loop is not too tight */
441 rc = pProgressAsync->WaitForCompletion(100);
442 if (FAILED(rc)) throw rc;
443 }
444 // report result of asynchronous operation
445 LONG iRc;
446 rc = pProgressAsync->COMGETTER(ResultCode)(&iRc);
447 if (FAILED(rc)) throw rc;
448
449
450 // if the thread of the progress object has an error, then
451 // retrieve the error info from there, or it'll be lost
452 if (FAILED(iRc))
453 {
454 ProgressErrorInfo info(pProgressAsync);
455 Utf8Str str(info.getText());
456 const char *pcsz = str.c_str();
457 HRESULT rc2 = setError(iRc, pcsz);
458 throw rc2;
459 }
460}
461
462void Appliance::addWarning(const char* aWarning, ...)
463{
464 va_list args;
465 va_start(args, aWarning);
466 Utf8StrFmtVA str(aWarning, args);
467 va_end(args);
468 m->llWarnings.push_back(str);
469}
470
471void Appliance::disksWeight(uint32_t &ulTotalMB, uint32_t &cDisks) const
472{
473 ulTotalMB = 0;
474 cDisks = 0;
475 /* Weigh the disk images according to their sizes */
476 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;
477 for (it = m->virtualSystemDescriptions.begin();
478 it != m->virtualSystemDescriptions.end();
479 ++it)
480 {
481 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);
482 /* One for every hard disk of the Virtual System */
483 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
484 std::list<VirtualSystemDescriptionEntry*>::const_iterator itH;
485 for (itH = avsdeHDs.begin();
486 itH != avsdeHDs.end();
487 ++itH)
488 {
489 const VirtualSystemDescriptionEntry *pHD = *itH;
490 ulTotalMB += pHD->ulSizeMB;
491 ++cDisks;
492 }
493 }
494
495}
496
497HRESULT Appliance::setUpProgressFS(ComObjPtr<Progress> &pProgress, const Bstr &bstrDescription)
498{
499 HRESULT rc;
500
501 /* Create the progress object */
502 pProgress.createObject();
503
504 /* Weigh the disk images according to their sizes */
505 uint32_t ulTotalMB;
506 uint32_t cDisks;
507 disksWeight(ulTotalMB, cDisks);
508
509 ULONG cOperations = 1 + cDisks; // one op per disk plus 1 for the XML
510
511 ULONG ulTotalOperationsWeight;
512 if (ulTotalMB)
513 {
514 m->ulWeightPerOperation = (ULONG)((double)ulTotalMB * 1 / 100); // use 1% of the progress for the XML
515 ulTotalOperationsWeight = ulTotalMB + m->ulWeightPerOperation;
516 }
517 else
518 {
519 // no disks to export:
520 ulTotalOperationsWeight = 1;
521 m->ulWeightPerOperation = 1;
522 }
523
524 Log(("Setting up progress object: ulTotalMB = %d, cDisks = %d, => cOperations = %d, ulTotalOperationsWeight = %d, m->ulWeightPerOperation = %d\n",
525 ulTotalMB, cDisks, cOperations, ulTotalOperationsWeight, m->ulWeightPerOperation));
526
527 rc = pProgress->init(mVirtualBox, static_cast<IAppliance*>(this),
528 bstrDescription,
529 TRUE /* aCancelable */,
530 cOperations, // ULONG cOperations,
531 ulTotalOperationsWeight, // ULONG ulTotalOperationsWeight,
532 bstrDescription, // CBSTR bstrFirstOperationDescription,
533 m->ulWeightPerOperation); // ULONG ulFirstOperationWeight,
534 return rc;
535}
536
537HRESULT Appliance::setUpProgressImportS3(ComObjPtr<Progress> &pProgress, const Bstr &bstrDescription)
538{
539 HRESULT rc;
540
541 /* Create the progress object */
542 pProgress.createObject();
543
544 /* Weigh the disk images according to their sizes */
545 uint32_t ulTotalMB;
546 uint32_t cDisks;
547 disksWeight(ulTotalMB, cDisks);
548
549 ULONG cOperations = 1 + 1 + 1 + cDisks; // one op per disk plus 1 for init, plus 1 for the manifest file & 1 plus for the import */
550
551 ULONG ulTotalOperationsWeight = ulTotalMB;
552 if (!ulTotalOperationsWeight)
553 // no disks to export:
554 ulTotalOperationsWeight = 1;
555
556 ULONG ulImportWeight = (ULONG)((double)ulTotalOperationsWeight * 50 / 100); // use 50% for import
557 ulTotalOperationsWeight += ulImportWeight;
558
559 m->ulWeightPerOperation = ulImportWeight; /* save for using later */
560
561 ULONG ulInitWeight = (ULONG)((double)ulTotalOperationsWeight * 0.1 / 100); // use 0.1% for init
562 ulTotalOperationsWeight += ulInitWeight;
563
564 Log(("Setting up progress object: ulTotalMB = %d, cDisks = %d, => cOperations = %d, ulTotalOperationsWeight = %d, m->ulWeightPerOperation = %d\n",
565 ulTotalMB, cDisks, cOperations, ulTotalOperationsWeight, m->ulWeightPerOperation));
566
567 rc = pProgress->init(mVirtualBox, static_cast<IAppliance*>(this),
568 bstrDescription,
569 TRUE /* aCancelable */,
570 cOperations, // ULONG cOperations,
571 ulTotalOperationsWeight, // ULONG ulTotalOperationsWeight,
572 Bstr(tr("Init")), // CBSTR bstrFirstOperationDescription,
573 ulInitWeight); // ULONG ulFirstOperationWeight,
574 return rc;
575}
576
577HRESULT Appliance::setUpProgressWriteS3(ComObjPtr<Progress> &pProgress, const Bstr &bstrDescription)
578{
579 HRESULT rc;
580
581 /* Create the progress object */
582 pProgress.createObject();
583
584 /* Weigh the disk images according to their sizes */
585 uint32_t ulTotalMB;
586 uint32_t cDisks;
587 disksWeight(ulTotalMB, cDisks);
588
589 ULONG cOperations = 1 + 1 + 1 + cDisks; // one op per disk plus 1 for the OVF, plus 1 for the mf & 1 plus to the temporary creation */
590
591 ULONG ulTotalOperationsWeight;
592 if (ulTotalMB)
593 {
594 m->ulWeightPerOperation = (ULONG)((double)ulTotalMB * 1 / 100); // use 1% of the progress for OVF file upload (we didn't know the size at this point)
595 ulTotalOperationsWeight = ulTotalMB + m->ulWeightPerOperation;
596 }
597 else
598 {
599 // no disks to export:
600 ulTotalOperationsWeight = 1;
601 m->ulWeightPerOperation = 1;
602 }
603 ULONG ulOVFCreationWeight = (ULONG)((double)ulTotalOperationsWeight * 50.0 / 100.0); /* Use 50% for the creation of the OVF & the disks */
604 ulTotalOperationsWeight += ulOVFCreationWeight;
605
606 Log(("Setting up progress object: ulTotalMB = %d, cDisks = %d, => cOperations = %d, ulTotalOperationsWeight = %d, m->ulWeightPerOperation = %d\n",
607 ulTotalMB, cDisks, cOperations, ulTotalOperationsWeight, m->ulWeightPerOperation));
608
609 rc = pProgress->init(mVirtualBox, static_cast<IAppliance*>(this),
610 bstrDescription,
611 TRUE /* aCancelable */,
612 cOperations, // ULONG cOperations,
613 ulTotalOperationsWeight, // ULONG ulTotalOperationsWeight,
614 bstrDescription, // CBSTR bstrFirstOperationDescription,
615 ulOVFCreationWeight); // ULONG ulFirstOperationWeight,
616 return rc;
617}
618
619void Appliance::parseURI(Utf8Str strUri, LocationInfo &locInfo) const
620{
621 /* Check the URI for the protocol */
622 if (strUri.startsWith("file://", Utf8Str::CaseInsensitive)) /* File based */
623 {
624 locInfo.storageType = VFSType_File;
625 strUri = strUri.substr(sizeof("file://") - 1);
626 }
627 else if (strUri.startsWith("SunCloud://", Utf8Str::CaseInsensitive)) /* Sun Cloud service */
628 {
629 locInfo.storageType = VFSType_S3;
630 strUri = strUri.substr(sizeof("SunCloud://") - 1);
631 }
632 else if (strUri.startsWith("S3://", Utf8Str::CaseInsensitive)) /* S3 service */
633 {
634 locInfo.storageType = VFSType_S3;
635 strUri = strUri.substr(sizeof("S3://") - 1);
636 }
637 else if (strUri.startsWith("webdav://", Utf8Str::CaseInsensitive)) /* webdav service */
638 throw E_NOTIMPL;
639
640 /* Not necessary on a file based URI */
641 if (locInfo.storageType != VFSType_File)
642 {
643 size_t uppos = strUri.find("@"); /* username:password combo */
644 if (uppos != Utf8Str::npos)
645 {
646 locInfo.strUsername = strUri.substr(0, uppos);
647 strUri = strUri.substr(uppos + 1);
648 size_t upos = locInfo.strUsername.find(":");
649 if (upos != Utf8Str::npos)
650 {
651 locInfo.strPassword = locInfo.strUsername.substr(upos + 1);
652 locInfo.strUsername = locInfo.strUsername.substr(0, upos);
653 }
654 }
655 size_t hpos = strUri.find("/"); /* hostname part */
656 if (hpos != Utf8Str::npos)
657 {
658 locInfo.strHostname = strUri.substr(0, hpos);
659 strUri = strUri.substr(hpos);
660 }
661 }
662
663 locInfo.strPath = strUri;
664}
665
666void Appliance::parseBucket(Utf8Str &aPath, Utf8Str &aBucket) const
667{
668 /* Buckets are S3 specific. So parse the bucket out of the file path */
669 if (!aPath.startsWith("/"))
670 throw setError(E_INVALIDARG,
671 tr("The path '%s' must start with /"), aPath.c_str());
672 size_t bpos = aPath.find("/", 1);
673 if (bpos != Utf8Str::npos)
674 {
675 aBucket = aPath.substr(1, bpos - 1); /* The bucket without any slashes */
676 aPath = aPath.substr(bpos); /* The rest of the file path */
677 }
678 /* If there is no bucket name provided reject it */
679 if (aBucket.isEmpty())
680 throw setError(E_INVALIDARG,
681 tr("You doesn't provide a bucket name in the URI '%s'"), aPath.c_str());
682}
683
684Utf8Str Appliance::manifestFileName(Utf8Str aPath) const
685{
686 /* Get the name part */
687 char *pszMfName = RTStrDup(RTPathFilename(aPath.c_str()));
688 /* Strip any extensions */
689 RTPathStripExt(pszMfName);
690 /* Path without the filename */
691 aPath.stripFilename();
692 /* Format the manifest path */
693 Utf8StrFmt strMfFile("%s/%s.mf", aPath.c_str(), pszMfName);
694 RTStrFree(pszMfName);
695 return strMfFile;
696}
697
698struct Appliance::TaskOVF
699{
700 TaskOVF(Appliance *aThat)
701 : pAppliance(aThat)
702 , rc(S_OK) {}
703
704 static int updateProgress(unsigned uPercent, void *pvUser);
705
706 LocationInfo locInfo;
707 Appliance *pAppliance;
708 ComObjPtr<Progress> progress;
709 HRESULT rc;
710};
711
712struct Appliance::TaskImportOVF: Appliance::TaskOVF
713{
714 enum TaskType
715 {
716 Read,
717 Import
718 };
719
720 TaskImportOVF(Appliance *aThat)
721 : TaskOVF(aThat)
722 , taskType(Read) {}
723
724 int startThread();
725
726 TaskType taskType;
727};
728
729struct Appliance::TaskExportOVF: Appliance::TaskOVF
730{
731 enum OVFFormat
732 {
733 unspecified,
734 OVF_0_9,
735 OVF_1_0
736 };
737 enum TaskType
738 {
739 Write
740 };
741
742 TaskExportOVF(Appliance *aThat)
743 : TaskOVF(aThat)
744 , taskType(Write) {}
745
746 int startThread();
747
748 TaskType taskType;
749 OVFFormat enFormat;
750};
751
752struct MyHardDiskAttachment
753{
754 Guid uuid;
755 ComPtr<IMachine> pMachine;
756 Bstr controllerType;
757 int32_t lChannel;
758 int32_t lDevice;
759};
760
761/* static */
762int Appliance::TaskOVF::updateProgress(unsigned uPercent, void *pvUser)
763{
764 Appliance::TaskOVF* pTask = *(Appliance::TaskOVF**)pvUser;
765
766 if (pTask &&
767 !pTask->progress.isNull())
768 {
769 BOOL fCanceled;
770 pTask->progress->COMGETTER(Canceled)(&fCanceled);
771 if (fCanceled)
772 return -1;
773 pTask->progress->setCurrentOperationProgress(uPercent);
774 }
775 return VINF_SUCCESS;
776}
777
778HRESULT Appliance::readImpl(const LocationInfo &aLocInfo, ComObjPtr<Progress> &aProgress)
779{
780 /* Initialize our worker task */
781 std::auto_ptr<TaskImportOVF> task(new TaskImportOVF(this));
782 /* What should the task do */
783 task->taskType = TaskImportOVF::Read;
784 /* Copy the current location info to the task */
785 task->locInfo = aLocInfo;
786
787 BstrFmt bstrDesc = BstrFmt(tr("Read appliance '%s'"),
788 aLocInfo.strPath.c_str());
789 HRESULT rc;
790 /* Create the progress object */
791 aProgress.createObject();
792 if (task->locInfo.storageType == VFSType_File)
793 {
794 /* 1 operation only */
795 rc = aProgress->init(mVirtualBox, static_cast<IAppliance*>(this),
796 bstrDesc,
797 TRUE /* aCancelable */);
798 }
799 else
800 {
801 /* 4/5 is downloading, 1/5 is reading */
802 rc = aProgress->init(mVirtualBox, static_cast<IAppliance*>(this),
803 bstrDesc,
804 TRUE /* aCancelable */,
805 2, // ULONG cOperations,
806 5, // ULONG ulTotalOperationsWeight,
807 BstrFmt(tr("Download appliance '%s'"),
808 aLocInfo.strPath.c_str()), // CBSTR bstrFirstOperationDescription,
809 4); // ULONG ulFirstOperationWeight,
810 }
811 if (FAILED(rc)) throw rc;
812
813 task->progress = aProgress;
814
815 rc = task->startThread();
816 CheckComRCThrowRC(rc);
817
818 /* Don't destruct on success */
819 task.release();
820
821 return rc;
822}
823
824HRESULT Appliance::importImpl(const LocationInfo &aLocInfo, ComObjPtr<Progress> &aProgress)
825{
826 /* Initialize our worker task */
827 std::auto_ptr<TaskImportOVF> task(new TaskImportOVF(this));
828 /* What should the task do */
829 task->taskType = TaskImportOVF::Import;
830 /* Copy the current location info to the task */
831 task->locInfo = aLocInfo;
832
833 Bstr progressDesc = BstrFmt(tr("Import appliance '%s'"),
834 aLocInfo.strPath.c_str());
835
836 HRESULT rc = S_OK;
837
838 /* todo: This progress init stuff should be done a little bit more generic */
839 if (task->locInfo.storageType == VFSType_File)
840 rc = setUpProgressFS(aProgress, progressDesc);
841 else
842 rc = setUpProgressImportS3(aProgress, progressDesc);
843 if (FAILED(rc)) throw rc;
844
845 task->progress = aProgress;
846
847 rc = task->startThread();
848 CheckComRCThrowRC(rc);
849
850 /* Don't destruct on success */
851 task.release();
852
853 return rc;
854}
855
856/**
857 * Worker thread implementation for Read() (ovf reader).
858 * @param aThread
859 * @param pvUser
860 */
861/* static */
862DECLCALLBACK(int) Appliance::taskThreadImportOVF(RTTHREAD /* aThread */, void *pvUser)
863{
864 std::auto_ptr<TaskImportOVF> task(static_cast<TaskImportOVF*>(pvUser));
865 AssertReturn(task.get(), VERR_GENERAL_FAILURE);
866
867 Appliance *pAppliance = task->pAppliance;
868
869 LogFlowFuncEnter();
870 LogFlowFunc(("Appliance %p\n", pAppliance));
871
872 HRESULT rc = S_OK;
873
874 switch(task->taskType)
875 {
876 case TaskImportOVF::Read:
877 {
878 if (task->locInfo.storageType == VFSType_File)
879 rc = pAppliance->readFS(task.get());
880 else if (task->locInfo.storageType == VFSType_S3)
881 rc = pAppliance->readS3(task.get());
882 break;
883 }
884 case TaskImportOVF::Import:
885 {
886 if (task->locInfo.storageType == VFSType_File)
887 rc = pAppliance->importFS(task.get());
888 else if (task->locInfo.storageType == VFSType_S3)
889 rc = pAppliance->importS3(task.get());
890 break;
891 }
892 }
893
894 LogFlowFunc(("rc=%Rhrc\n", rc));
895 LogFlowFuncLeave();
896
897 return VINF_SUCCESS;
898}
899
900int Appliance::TaskImportOVF::startThread()
901{
902 int vrc = RTThreadCreate(NULL, Appliance::taskThreadImportOVF, this,
903 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
904 "Appliance::Task");
905
906 ComAssertMsgRCRet(vrc,
907 ("Could not create taskThreadImportOVF (%Rrc)\n", vrc), E_FAIL);
908
909 return S_OK;
910}
911
912int Appliance::readFS(TaskImportOVF *pTask)
913{
914 LogFlowFuncEnter();
915 LogFlowFunc(("Appliance %p\n", this));
916
917 AutoCaller autoCaller(this);
918 CheckComRCReturnRC(autoCaller.rc());
919
920 AutoWriteLock appLock(this);
921
922 HRESULT rc = S_OK;
923
924 try
925 {
926 /* Read & parse the XML structure of the OVF file */
927 m->pReader = new OVFReader(pTask->locInfo.strPath);
928 /* Create the SHA1 sum of the OVF file for later validation */
929 char *pszDigest;
930 int vrc = RTSha1Digest(pTask->locInfo.strPath.c_str(), &pszDigest);
931 if (RT_FAILURE(vrc))
932 throw setError(VBOX_E_FILE_ERROR,
933 tr("Couldn't calculate SHA1 digest for file '%s' (%Rrc)"),
934 RTPathFilename(pTask->locInfo.strPath.c_str()), vrc);
935 m->strOVFSHA1Digest = pszDigest;
936 RTStrFree(pszDigest);
937 }
938 catch(xml::Error &x)
939 {
940 rc = setError(VBOX_E_FILE_ERROR,
941 x.what());
942 }
943
944 pTask->rc = rc;
945
946 if (!pTask->progress.isNull())
947 pTask->progress->notifyComplete(rc);
948
949 LogFlowFunc(("rc=%Rhrc\n", rc));
950 LogFlowFuncLeave();
951
952 return VINF_SUCCESS;
953}
954
955int Appliance::readS3(TaskImportOVF *pTask)
956{
957 LogFlowFuncEnter();
958 LogFlowFunc(("Appliance %p\n", this));
959
960 AutoCaller autoCaller(this);
961 CheckComRCReturnRC(autoCaller.rc());
962
963 AutoWriteLock appLock(this);
964
965 HRESULT rc = S_OK;
966 int vrc = VINF_SUCCESS;
967 RTS3 hS3 = NIL_RTS3;
968 char szOSTmpDir[RTPATH_MAX];
969 RTPathTemp(szOSTmpDir, sizeof(szOSTmpDir));
970 /* The template for the temporary directory created below */
971 char *pszTmpDir;
972 RTStrAPrintf(&pszTmpDir, "%s"RTPATH_SLASH_STR"vbox-ovf-XXXXXX", szOSTmpDir);
973 list< pair<Utf8Str, ULONG> > filesList;
974 Utf8Str strTmpOvf;
975
976 try
977 {
978 /* Extract the bucket */
979 Utf8Str tmpPath = pTask->locInfo.strPath;
980 Utf8Str bucket;
981 parseBucket(tmpPath, bucket);
982
983 /* We need a temporary directory which we can put the OVF file & all
984 * disk images in */
985 vrc = RTDirCreateTemp(pszTmpDir);
986 if (RT_FAILURE(rc))
987 throw setError(VBOX_E_FILE_ERROR,
988 tr("Cannot create temporary directory '%s'"), pszTmpDir);
989
990 /* The temporary name of the target OVF file */
991 strTmpOvf = Utf8StrFmt("%s/%s", pszTmpDir, RTPathFilename(tmpPath));
992
993 /* Next we have to download the OVF */
994 vrc = RTS3Create(&hS3, pTask->locInfo.strUsername.c_str(), pTask->locInfo.strPassword.c_str(), pTask->locInfo.strHostname.c_str(), "virtualbox-agent/"VBOX_VERSION_STRING);
995 if(RT_FAILURE(vrc))
996 throw setError(VBOX_E_IPRT_ERROR,
997 tr("Cannot create S3 service handler"));
998 RTS3SetProgressCallback(hS3, pTask->updateProgress, &pTask);
999
1000 /* Get it */
1001 char *pszFilename = RTPathFilename(strTmpOvf.c_str());
1002 vrc = RTS3GetKey(hS3, bucket.c_str(), pszFilename, strTmpOvf.c_str());
1003 if (RT_FAILURE(vrc))
1004 {
1005 if(vrc == VERR_S3_CANCELED)
1006 throw S_OK; /* todo: !!!!!!!!!!!!! */
1007 else if(vrc == VERR_S3_ACCESS_DENIED)
1008 throw setError(E_ACCESSDENIED,
1009 tr("Cannot download file '%s' from S3 storage server (Access denied)"), pszFilename);
1010 else if(vrc == VERR_S3_NOT_FOUND)
1011 throw setError(VBOX_E_FILE_ERROR,
1012 tr("Cannot download file '%s' from S3 storage server (File not found)"), pszFilename);
1013 else
1014 throw setError(VBOX_E_IPRT_ERROR,
1015 tr("Cannot download file '%s' from S3 storage server (%Rrc)"), pszFilename, vrc);
1016 }
1017
1018 /* Close the connection early */
1019 RTS3Destroy(hS3);
1020 hS3 = NIL_RTS3;
1021
1022 if (!pTask->progress.isNull())
1023 pTask->progress->setNextOperation(Bstr(tr("Reading")), 1);
1024
1025 /* Prepare the temporary reading of the OVF */
1026 ComObjPtr<Progress> progress;
1027 LocationInfo li;
1028 li.strPath = strTmpOvf;
1029 /* Start the reading from the fs */
1030 rc = readImpl(li, progress);
1031 if (FAILED(rc)) throw rc;
1032
1033 /* Unlock the appliance for the reading thread */
1034 appLock.unlock();
1035 /* Wait until the reading is done, but report the progress back to the
1036 caller */
1037 ComPtr<IProgress> progressInt(progress);
1038 waitForAsyncProgress(pTask->progress, progressInt); /* Any errors will be thrown */
1039
1040 /* Again lock the appliance for the next steps */
1041 appLock.lock();
1042 }
1043 catch(HRESULT aRC)
1044 {
1045 rc = aRC;
1046 }
1047 /* Cleanup */
1048 RTS3Destroy(hS3);
1049 /* Delete all files which where temporary created */
1050 if (RTPathExists(strTmpOvf.c_str()))
1051 {
1052 vrc = RTFileDelete(strTmpOvf.c_str());
1053 if(RT_FAILURE(vrc))
1054 rc = setError(VBOX_E_FILE_ERROR,
1055 tr("Cannot delete file '%s' (%Rrc)"), strTmpOvf.c_str(), vrc);
1056 }
1057 /* Delete the temporary directory */
1058 if (RTPathExists(pszTmpDir))
1059 {
1060 vrc = RTDirRemove(pszTmpDir);
1061 if(RT_FAILURE(vrc))
1062 rc = setError(VBOX_E_FILE_ERROR,
1063 tr("Cannot delete temporary directory '%s' (%Rrc)"), pszTmpDir, vrc);
1064 }
1065 if (pszTmpDir)
1066 RTStrFree(pszTmpDir);
1067
1068 pTask->rc = rc;
1069
1070 if (!pTask->progress.isNull())
1071 pTask->progress->notifyComplete(rc);
1072
1073 LogFlowFunc(("rc=%Rhrc\n", rc));
1074 LogFlowFuncLeave();
1075
1076 return VINF_SUCCESS;
1077}
1078
1079int Appliance::importFS(TaskImportOVF *pTask)
1080{
1081 LogFlowFuncEnter();
1082 LogFlowFunc(("Appliance %p\n", this));
1083
1084 AutoCaller autoCaller(this);
1085 CheckComRCReturnRC(autoCaller.rc());
1086
1087 AutoWriteLock appLock(this);
1088
1089 HRESULT rc = S_OK;
1090
1091 // rollback for errors:
1092 // a list of images that we created/imported
1093 list<MyHardDiskAttachment> llHardDiskAttachments;
1094 list< ComPtr<IHardDisk> > llHardDisksCreated;
1095 list<Guid> llMachinesRegistered;
1096
1097 ComPtr<ISession> session;
1098 bool fSessionOpen = false;
1099 rc = session.createInprocObject(CLSID_Session);
1100 CheckComRCReturnRC(rc);
1101
1102 const OVFReader reader = *m->pReader;
1103 // this is safe to access because this thread only gets started
1104 // if pReader != NULL
1105
1106 /* If an manifest file exists, verify the content. Therefor we need all
1107 * files which are referenced by the OVF & the OVF itself */
1108 Utf8Str strMfFile = manifestFileName(pTask->locInfo.strPath);
1109 list<Utf8Str> filesList;
1110 if (RTPathExists(strMfFile.c_str()))
1111 {
1112 Utf8Str strSrcDir(pTask->locInfo.strPath);
1113 strSrcDir.stripFilename();
1114 /* Add every disks of every virtual system to an internal list */
1115 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;
1116 for (it = m->virtualSystemDescriptions.begin();
1117 it != m->virtualSystemDescriptions.end();
1118 ++it)
1119 {
1120 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);
1121 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
1122 std::list<VirtualSystemDescriptionEntry*>::const_iterator itH;
1123 for (itH = avsdeHDs.begin();
1124 itH != avsdeHDs.end();
1125 ++itH)
1126 {
1127 VirtualSystemDescriptionEntry *vsdeHD = *itH;
1128 /* Find the disk from the OVF's disk list */
1129 DiskImagesMap::const_iterator itDiskImage = reader.m_mapDisks.find(vsdeHD->strRef);
1130 const DiskImage &di = itDiskImage->second;
1131 Utf8StrFmt strSrcFilePath("%s%c%s", strSrcDir.c_str(), RTPATH_DELIMITER, di.strHref.c_str());
1132 filesList.push_back(strSrcFilePath);
1133 }
1134 }
1135 /* Create the test list */
1136 PRTMANIFESTTEST pTestList = (PRTMANIFESTTEST)RTMemAllocZ(sizeof(RTMANIFESTTEST)*(filesList.size()+1));
1137 pTestList[0].pszTestFile = (char*)pTask->locInfo.strPath.c_str();
1138 pTestList[0].pszTestDigest = (char*)m->strOVFSHA1Digest.c_str();
1139 int vrc = VINF_SUCCESS;
1140 size_t i = 1;
1141 list<Utf8Str>::const_iterator it1;
1142 for (it1 = filesList.begin();
1143 it1 != filesList.end();
1144 ++it1, ++i)
1145 {
1146 char* pszDigest;
1147 vrc = RTSha1Digest((*it1).c_str(), &pszDigest);
1148 pTestList[i].pszTestFile = (char*)(*it1).c_str();
1149 pTestList[i].pszTestDigest = pszDigest;
1150 }
1151 size_t cIndexOnError;
1152 vrc = RTManifestVerify(strMfFile.c_str(), pTestList, filesList.size() + 1, &cIndexOnError);
1153 if (vrc == VERR_MANIFEST_DIGEST_MISMATCH)
1154 rc = setError(VBOX_E_FILE_ERROR,
1155 tr("The SHA1 digest of '%s' doesn't match to the one in '%s'"),
1156 RTPathFilename(pTestList[cIndexOnError].pszTestFile),
1157 RTPathFilename(strMfFile.c_str()));
1158 else if (RT_FAILURE(vrc))
1159 rc = setError(VBOX_E_FILE_ERROR,
1160 tr("Couldn't verify the content of '%s' against the available files (%Rrc)"),
1161 RTPathFilename(strMfFile.c_str()),
1162 vrc);
1163 /* Cleanup */
1164 for (size_t i=1; i < filesList.size(); ++i)
1165 RTStrFree(pTestList[i].pszTestDigest);
1166 RTMemFree(pTestList);
1167 if (FAILED(rc))
1168 {
1169 /* Return on error */
1170 pTask->rc = rc;
1171
1172 if (!pTask->progress.isNull())
1173 pTask->progress->notifyComplete(rc);
1174 return rc;
1175 }
1176 }
1177
1178 list<VirtualSystem>::const_iterator it;
1179 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it1;
1180 /* Iterate through all virtual systems of that appliance */
1181 size_t i = 0;
1182 for (it = reader.m_llVirtualSystems.begin(),
1183 it1 = m->virtualSystemDescriptions.begin();
1184 it != reader.m_llVirtualSystems.end();
1185 ++it, ++it1, ++i)
1186 {
1187 const VirtualSystem &vsysThis = *it;
1188 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it1);
1189
1190 ComPtr<IMachine> pNewMachine;
1191
1192 /* Catch possible errors */
1193 try
1194 {
1195 /* Guest OS type */
1196 std::list<VirtualSystemDescriptionEntry*> vsdeOS;
1197 vsdeOS = vsdescThis->findByType(VirtualSystemDescriptionType_OS);
1198 if (vsdeOS.size() < 1)
1199 throw setError(VBOX_E_FILE_ERROR,
1200 tr("Missing guest OS type"));
1201 const Utf8Str &strOsTypeVBox = vsdeOS.front()->strVbox;
1202
1203 /* Now that we know the base system get our internal defaults based on that. */
1204 ComPtr<IGuestOSType> osType;
1205 rc = mVirtualBox->GetGuestOSType(Bstr(strOsTypeVBox), osType.asOutParam());
1206 if (FAILED(rc)) throw rc;
1207
1208 /* Create the machine */
1209 /* First get the name */
1210 std::list<VirtualSystemDescriptionEntry*> vsdeName = vsdescThis->findByType(VirtualSystemDescriptionType_Name);
1211 if (vsdeName.size() < 1)
1212 throw setError(VBOX_E_FILE_ERROR,
1213 tr("Missing VM name"));
1214 const Utf8Str &strNameVBox = vsdeName.front()->strVbox;
1215 rc = mVirtualBox->CreateMachine(Bstr(strNameVBox), Bstr(strOsTypeVBox),
1216 Bstr(), Bstr(),
1217 pNewMachine.asOutParam());
1218 if (FAILED(rc)) throw rc;
1219
1220 // and the description
1221 std::list<VirtualSystemDescriptionEntry*> vsdeDescription = vsdescThis->findByType(VirtualSystemDescriptionType_Description);
1222 if (vsdeDescription.size())
1223 {
1224 const Utf8Str &strDescription = vsdeDescription.front()->strVbox;
1225 rc = pNewMachine->COMSETTER(Description)(Bstr(strDescription));
1226 if (FAILED(rc)) throw rc;
1227 }
1228
1229 /* CPU count */
1230 std::list<VirtualSystemDescriptionEntry*> vsdeCPU = vsdescThis->findByType (VirtualSystemDescriptionType_CPU);
1231 ComAssertMsgThrow(vsdeCPU.size() == 1, ("CPU count missing"), E_FAIL);
1232 const Utf8Str &cpuVBox = vsdeCPU.front()->strVbox;
1233 ULONG tmpCount = (ULONG)RTStrToUInt64(cpuVBox.c_str());
1234 rc = pNewMachine->COMSETTER(CPUCount)(tmpCount);
1235 if (FAILED(rc)) throw rc;
1236 bool fEnableIOApic = false;
1237 /* We need HWVirt & IO-APIC if more than one CPU is requested */
1238 if (tmpCount > 1)
1239 {
1240 rc = pNewMachine->COMSETTER(HWVirtExEnabled)(TRUE);
1241 if (FAILED(rc)) throw rc;
1242
1243 fEnableIOApic = true;
1244 }
1245
1246 /* RAM */
1247 std::list<VirtualSystemDescriptionEntry*> vsdeRAM = vsdescThis->findByType(VirtualSystemDescriptionType_Memory);
1248 ComAssertMsgThrow(vsdeRAM.size() == 1, ("RAM size missing"), E_FAIL);
1249 const Utf8Str &memoryVBox = vsdeRAM.front()->strVbox;
1250 ULONG tt = (ULONG)RTStrToUInt64(memoryVBox.c_str());
1251 rc = pNewMachine->COMSETTER(MemorySize)(tt);
1252 if (FAILED(rc)) throw rc;
1253
1254 /* VRAM */
1255 /* Get the recommended VRAM for this guest OS type */
1256 ULONG vramVBox;
1257 rc = osType->COMGETTER(RecommendedVRAM)(&vramVBox);
1258 if (FAILED(rc)) throw rc;
1259
1260 /* Set the VRAM */
1261 rc = pNewMachine->COMSETTER(VRAMSize)(vramVBox);
1262 if (FAILED(rc)) throw rc;
1263
1264 /* I/O APIC: so far we have no setting for this. Enable it if we
1265 import a Windows VM because if if Windows was installed without IOAPIC,
1266 it will not mind finding an one later on, but if Windows was installed
1267 _with_ an IOAPIC, it will bluescreen if it's not found */
1268 Bstr bstrFamilyId;
1269 rc = osType->COMGETTER(FamilyId)(bstrFamilyId.asOutParam());
1270 if (FAILED(rc)) throw rc;
1271
1272 Utf8Str strFamilyId(bstrFamilyId);
1273 if (strFamilyId == "Windows")
1274 fEnableIOApic = true;
1275
1276 /* If IP-APIC should be enabled could be have different reasons.
1277 See CPU count & the Win test above. Here we enable it if it was
1278 previously requested. */
1279 if (fEnableIOApic)
1280 {
1281 ComPtr<IBIOSSettings> pBIOSSettings;
1282 rc = pNewMachine->COMGETTER(BIOSSettings)(pBIOSSettings.asOutParam());
1283 if (FAILED(rc)) throw rc;
1284
1285 rc = pBIOSSettings->COMSETTER(IOAPICEnabled)(TRUE);
1286 if (FAILED(rc)) throw rc;
1287 }
1288
1289 /* Audio Adapter */
1290 std::list<VirtualSystemDescriptionEntry*> vsdeAudioAdapter = vsdescThis->findByType(VirtualSystemDescriptionType_SoundCard);
1291 /* @todo: we support one audio adapter only */
1292 if (vsdeAudioAdapter.size() > 0)
1293 {
1294 const Utf8Str& audioAdapterVBox = vsdeAudioAdapter.front()->strVbox;
1295 if (audioAdapterVBox.compare("null", Utf8Str::CaseInsensitive) != 0)
1296 {
1297 uint32_t audio = RTStrToUInt32(audioAdapterVBox.c_str());
1298 ComPtr<IAudioAdapter> audioAdapter;
1299 rc = pNewMachine->COMGETTER(AudioAdapter)(audioAdapter.asOutParam());
1300 if (FAILED(rc)) throw rc;
1301 rc = audioAdapter->COMSETTER(Enabled)(true);
1302 if (FAILED(rc)) throw rc;
1303 rc = audioAdapter->COMSETTER(AudioController)(static_cast<AudioControllerType_T>(audio));
1304 if (FAILED(rc)) throw rc;
1305 }
1306 }
1307
1308#ifdef VBOX_WITH_USB
1309 /* USB Controller */
1310 std::list<VirtualSystemDescriptionEntry*> vsdeUSBController = vsdescThis->findByType(VirtualSystemDescriptionType_USBController);
1311 // USB support is enabled if there's at least one such entry; to disable USB support,
1312 // the type of the USB item would have been changed to "ignore"
1313 bool fUSBEnabled = vsdeUSBController.size() > 0;
1314
1315 ComPtr<IUSBController> usbController;
1316 rc = pNewMachine->COMGETTER(USBController)(usbController.asOutParam());
1317 if (FAILED(rc)) throw rc;
1318 rc = usbController->COMSETTER(Enabled)(fUSBEnabled);
1319 if (FAILED(rc)) throw rc;
1320#endif /* VBOX_WITH_USB */
1321
1322 /* Change the network adapters */
1323 std::list<VirtualSystemDescriptionEntry*> vsdeNW = vsdescThis->findByType(VirtualSystemDescriptionType_NetworkAdapter);
1324 if (vsdeNW.size() == 0)
1325 {
1326 /* No network adapters, so we have to disable our default one */
1327 ComPtr<INetworkAdapter> nwVBox;
1328 rc = pNewMachine->GetNetworkAdapter(0, nwVBox.asOutParam());
1329 if (FAILED(rc)) throw rc;
1330 rc = nwVBox->COMSETTER(Enabled)(false);
1331 if (FAILED(rc)) throw rc;
1332 }
1333 else
1334 {
1335 list<VirtualSystemDescriptionEntry*>::const_iterator nwIt;
1336 /* Iterate through all network cards. We support 8 network adapters
1337 * at the maximum. (@todo: warn if there are more!) */
1338 size_t a = 0;
1339 for (nwIt = vsdeNW.begin();
1340 (nwIt != vsdeNW.end() && a < SchemaDefs::NetworkAdapterCount);
1341 ++nwIt, ++a)
1342 {
1343 const VirtualSystemDescriptionEntry* pvsys = *nwIt;
1344
1345 const Utf8Str &nwTypeVBox = pvsys->strVbox;
1346 uint32_t tt1 = RTStrToUInt32(nwTypeVBox.c_str());
1347 ComPtr<INetworkAdapter> pNetworkAdapter;
1348 rc = pNewMachine->GetNetworkAdapter((ULONG)a, pNetworkAdapter.asOutParam());
1349 if (FAILED(rc)) throw rc;
1350 /* Enable the network card & set the adapter type */
1351 rc = pNetworkAdapter->COMSETTER(Enabled)(true);
1352 if (FAILED(rc)) throw rc;
1353 rc = pNetworkAdapter->COMSETTER(AdapterType)(static_cast<NetworkAdapterType_T>(tt1));
1354 if (FAILED(rc)) throw rc;
1355
1356 // default is NAT; change to "bridged" if extra conf says so
1357 if (!pvsys->strExtraConfig.compare("type=Bridged", Utf8Str::CaseInsensitive))
1358 {
1359 /* Attach to the right interface */
1360 rc = pNetworkAdapter->AttachToBridgedInterface();
1361 if (FAILED(rc)) throw rc;
1362 ComPtr<IHost> host;
1363 rc = mVirtualBox->COMGETTER(Host)(host.asOutParam());
1364 if (FAILED(rc)) throw rc;
1365 com::SafeIfaceArray<IHostNetworkInterface> nwInterfaces;
1366 rc = host->COMGETTER(NetworkInterfaces)(ComSafeArrayAsOutParam(nwInterfaces));
1367 if (FAILED(rc)) throw rc;
1368 /* We search for the first host network interface which
1369 * is usable for bridged networking */
1370 for (size_t i=0; i < nwInterfaces.size(); ++i)
1371 {
1372 HostNetworkInterfaceType_T itype;
1373 rc = nwInterfaces[i]->COMGETTER(InterfaceType)(&itype);
1374 if (FAILED(rc)) throw rc;
1375 if (itype == HostNetworkInterfaceType_Bridged)
1376 {
1377 Bstr name;
1378 rc = nwInterfaces[i]->COMGETTER(Name)(name.asOutParam());
1379 if (FAILED(rc)) throw rc;
1380 /* Set the interface name to attach to */
1381 pNetworkAdapter->COMSETTER(HostInterface)(name);
1382 if (FAILED(rc)) throw rc;
1383 break;
1384 }
1385 }
1386 }
1387 /* Next test for host only interfaces */
1388 else if (!pvsys->strExtraConfig.compare("type=HostOnly", Utf8Str::CaseInsensitive))
1389 {
1390 /* Attach to the right interface */
1391 rc = pNetworkAdapter->AttachToHostOnlyInterface();
1392 if (FAILED(rc)) throw rc;
1393 ComPtr<IHost> host;
1394 rc = mVirtualBox->COMGETTER(Host)(host.asOutParam());
1395 if (FAILED(rc)) throw rc;
1396 com::SafeIfaceArray<IHostNetworkInterface> nwInterfaces;
1397 rc = host->COMGETTER(NetworkInterfaces)(ComSafeArrayAsOutParam(nwInterfaces));
1398 if (FAILED(rc)) throw rc;
1399 /* We search for the first host network interface which
1400 * is usable for host only networking */
1401 for (size_t i=0; i < nwInterfaces.size(); ++i)
1402 {
1403 HostNetworkInterfaceType_T itype;
1404 rc = nwInterfaces[i]->COMGETTER(InterfaceType)(&itype);
1405 if (FAILED(rc)) throw rc;
1406 if (itype == HostNetworkInterfaceType_HostOnly)
1407 {
1408 Bstr name;
1409 rc = nwInterfaces[i]->COMGETTER(Name)(name.asOutParam());
1410 if (FAILED(rc)) throw rc;
1411 /* Set the interface name to attach to */
1412 pNetworkAdapter->COMSETTER(HostInterface)(name);
1413 if (FAILED(rc)) throw rc;
1414 break;
1415 }
1416 }
1417 }
1418 }
1419 }
1420
1421 /* Floppy drive */
1422 std::list<VirtualSystemDescriptionEntry*> vsdeFloppy = vsdescThis->findByType(VirtualSystemDescriptionType_Floppy);
1423 // Floppy support is enabled if there's at least one such entry; to disable floppy support,
1424 // the type of the floppy item would have been changed to "ignore"
1425 bool fFloppyEnabled = vsdeFloppy.size() > 0;
1426 ComPtr<IFloppyDrive> floppyDrive;
1427 rc = pNewMachine->COMGETTER(FloppyDrive)(floppyDrive.asOutParam());
1428 if (FAILED(rc)) throw rc;
1429 rc = floppyDrive->COMSETTER(Enabled)(fFloppyEnabled);
1430 if (FAILED(rc)) throw rc;
1431
1432 /* CDROM drive */
1433 /* @todo: I can't disable the CDROM. So nothing to do for now */
1434 // std::list<VirtualSystemDescriptionEntry*> vsdeFloppy = vsd->findByType(VirtualSystemDescriptionType_CDROM);
1435
1436 /* Hard disk controller IDE */
1437 std::list<VirtualSystemDescriptionEntry*> vsdeHDCIDE = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerIDE);
1438 if (vsdeHDCIDE.size() > 1)
1439 throw setError(VBOX_E_FILE_ERROR,
1440 tr("Too many IDE controllers in OVF; VirtualBox only supports one"));
1441 if (vsdeHDCIDE.size() == 1)
1442 {
1443 ComPtr<IStorageController> pController;
1444 rc = pNewMachine->GetStorageControllerByName(Bstr("IDE"), pController.asOutParam());
1445 if (FAILED(rc)) throw rc;
1446
1447 const char *pcszIDEType = vsdeHDCIDE.front()->strVbox.c_str();
1448 if (!strcmp(pcszIDEType, "PIIX3"))
1449 rc = pController->COMSETTER(ControllerType)(StorageControllerType_PIIX3);
1450 else if (!strcmp(pcszIDEType, "PIIX4"))
1451 rc = pController->COMSETTER(ControllerType)(StorageControllerType_PIIX4);
1452 else if (!strcmp(pcszIDEType, "ICH6"))
1453 rc = pController->COMSETTER(ControllerType)(StorageControllerType_ICH6);
1454 else
1455 throw setError(VBOX_E_FILE_ERROR,
1456 tr("Invalid IDE controller type \"%s\""),
1457 pcszIDEType);
1458 if (FAILED(rc)) throw rc;
1459 }
1460#ifdef VBOX_WITH_AHCI
1461 /* Hard disk controller SATA */
1462 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSATA = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSATA);
1463 if (vsdeHDCSATA.size() > 1)
1464 throw setError(VBOX_E_FILE_ERROR,
1465 tr("Too many SATA controllers in OVF; VirtualBox only supports one"));
1466 if (vsdeHDCSATA.size() > 0)
1467 {
1468 ComPtr<IStorageController> pController;
1469 const Utf8Str &hdcVBox = vsdeHDCSATA.front()->strVbox;
1470 if (hdcVBox == "AHCI")
1471 {
1472 rc = pNewMachine->AddStorageController(Bstr("SATA"), StorageBus_SATA, pController.asOutParam());
1473 if (FAILED(rc)) throw rc;
1474 }
1475 else
1476 throw setError(VBOX_E_FILE_ERROR,
1477 tr("Invalid SATA controller type \"%s\""),
1478 hdcVBox.c_str());
1479 }
1480#endif /* VBOX_WITH_AHCI */
1481
1482#ifdef VBOX_WITH_LSILOGIC
1483 /* Hard disk controller SCSI */
1484 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSCSI = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSCSI);
1485 if (vsdeHDCSCSI.size() > 1)
1486 throw setError(VBOX_E_FILE_ERROR,
1487 tr("Too many SCSI controllers in OVF; VirtualBox only supports one"));
1488 if (vsdeHDCSCSI.size() > 0)
1489 {
1490 ComPtr<IStorageController> pController;
1491 StorageControllerType_T controllerType;
1492 const Utf8Str &hdcVBox = vsdeHDCSCSI.front()->strVbox;
1493 if (hdcVBox == "LsiLogic")
1494 controllerType = StorageControllerType_LsiLogic;
1495 else if (hdcVBox == "BusLogic")
1496 controllerType = StorageControllerType_BusLogic;
1497 else
1498 throw setError(VBOX_E_FILE_ERROR,
1499 tr("Invalid SCSI controller type \"%s\""),
1500 hdcVBox.c_str());
1501
1502 rc = pNewMachine->AddStorageController(Bstr("SCSI"), StorageBus_SCSI, pController.asOutParam());
1503 if (FAILED(rc)) throw rc;
1504 rc = pController->COMSETTER(ControllerType)(controllerType);
1505 if (FAILED(rc)) throw rc;
1506 }
1507#endif /* VBOX_WITH_LSILOGIC */
1508
1509 /* Now its time to register the machine before we add any hard disks */
1510 rc = mVirtualBox->RegisterMachine(pNewMachine);
1511 if (FAILED(rc)) throw rc;
1512
1513 Bstr newMachineId_;
1514 rc = pNewMachine->COMGETTER(Id)(newMachineId_.asOutParam());
1515 if (FAILED(rc)) throw rc;
1516 Guid newMachineId(newMachineId_);
1517
1518 // store new machine for roll-back in case of errors
1519 llMachinesRegistered.push_back(newMachineId);
1520
1521 /* Create the hard disks & connect them to the appropriate controllers. */
1522 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
1523 if (avsdeHDs.size() > 0)
1524 {
1525 /* If in the next block an error occur we have to deregister
1526 the machine, so make an extra try/catch block. */
1527 ComPtr<IHardDisk> srcHdVBox;
1528 bool fSourceHdNeedsClosing = false;
1529
1530 try
1531 {
1532 /* In order to attach hard disks we need to open a session
1533 * for the new machine */
1534 rc = mVirtualBox->OpenSession(session, newMachineId_);
1535 if (FAILED(rc)) throw rc;
1536 fSessionOpen = true;
1537
1538 /* The disk image has to be on the same place as the OVF file. So
1539 * strip the filename out of the full file path. */
1540 Utf8Str strSrcDir(pTask->locInfo.strPath);
1541 strSrcDir.stripFilename();
1542
1543 /* Iterate over all given disk images */
1544 list<VirtualSystemDescriptionEntry*>::const_iterator itHD;
1545 for (itHD = avsdeHDs.begin();
1546 itHD != avsdeHDs.end();
1547 ++itHD)
1548 {
1549 VirtualSystemDescriptionEntry *vsdeHD = *itHD;
1550
1551 const char *pcszDstFilePath = vsdeHD->strVbox.c_str();
1552 /* Check if the destination file exists already or the
1553 * destination path is empty. */
1554 if ( !(*pcszDstFilePath)
1555 || RTPathExists(pcszDstFilePath)
1556 )
1557 /* This isn't allowed */
1558 throw setError(VBOX_E_FILE_ERROR,
1559 tr("Destination file '%s' exists",
1560 pcszDstFilePath));
1561
1562 /* Find the disk from the OVF's disk list */
1563 DiskImagesMap::const_iterator itDiskImage = reader.m_mapDisks.find(vsdeHD->strRef);
1564 /* vsdeHD->strRef contains the disk identifier (e.g. "vmdisk1"), which should exist
1565 in the virtual system's disks map under that ID and also in the global images map. */
1566 VirtualDisksMap::const_iterator itVirtualDisk = vsysThis.mapVirtualDisks.find(vsdeHD->strRef);
1567
1568 if ( itDiskImage == reader.m_mapDisks.end()
1569 || itVirtualDisk == vsysThis.mapVirtualDisks.end()
1570 )
1571 throw setError(E_FAIL,
1572 tr("Internal inconsistency looking up disk images."));
1573
1574 const DiskImage &di = itDiskImage->second;
1575 const VirtualDisk &vd = itVirtualDisk->second;
1576
1577 /* Make sure all target directories exists */
1578 rc = VirtualBox::ensureFilePathExists(pcszDstFilePath);
1579 if (FAILED(rc))
1580 throw rc;
1581
1582 // subprogress object for hard disk
1583 ComPtr<IProgress> pProgress2;
1584
1585 ComPtr<IHardDisk> dstHdVBox;
1586 /* If strHref is empty we have to create a new file */
1587 if (di.strHref.isEmpty())
1588 {
1589 /* Which format to use? */
1590 Bstr srcFormat = L"VDI";
1591 if ( di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#sparse", Utf8Str::CaseInsensitive)
1592 || di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive))
1593 srcFormat = L"VMDK";
1594 /* Create an empty hard disk */
1595 rc = mVirtualBox->CreateHardDisk(srcFormat, Bstr(pcszDstFilePath), dstHdVBox.asOutParam());
1596 if (FAILED(rc)) throw rc;
1597
1598 /* Create a dynamic growing disk image with the given capacity */
1599 rc = dstHdVBox->CreateBaseStorage(di.iCapacity / _1M, HardDiskVariant_Standard, pProgress2.asOutParam());
1600 if (FAILED(rc)) throw rc;
1601
1602 /* Advance to the next operation */
1603 if (!pTask->progress.isNull())
1604 pTask->progress->setNextOperation(BstrFmt(tr("Creating virtual disk image '%s'"), pcszDstFilePath),
1605 vsdeHD->ulSizeMB); // operation's weight, as set up with the IProgress originally
1606 }
1607 else
1608 {
1609 /* Construct the source file path */
1610 Utf8StrFmt strSrcFilePath("%s%c%s", strSrcDir.c_str(), RTPATH_DELIMITER, di.strHref.c_str());
1611 /* Check if the source file exists */
1612 if (!RTPathExists(strSrcFilePath.c_str()))
1613 /* This isn't allowed */
1614 throw setError(VBOX_E_FILE_ERROR,
1615 tr("Source virtual disk image file '%s' doesn't exist"),
1616 strSrcFilePath.c_str());
1617
1618 /* Clone the disk image (this is necessary cause the id has
1619 * to be recreated for the case the same hard disk is
1620 * attached already from a previous import) */
1621
1622 /* First open the existing disk image */
1623 rc = mVirtualBox->OpenHardDisk(Bstr(strSrcFilePath),
1624 AccessMode_ReadOnly,
1625 false, Bstr(""), false, Bstr(""),
1626 srcHdVBox.asOutParam());
1627 if (FAILED(rc)) throw rc;
1628 fSourceHdNeedsClosing = true;
1629
1630 /* We need the format description of the source disk image */
1631 Bstr srcFormat;
1632 rc = srcHdVBox->COMGETTER(Format)(srcFormat.asOutParam());
1633 if (FAILED(rc)) throw rc;
1634 /* Create a new hard disk interface for the destination disk image */
1635 rc = mVirtualBox->CreateHardDisk(srcFormat, Bstr(pcszDstFilePath), dstHdVBox.asOutParam());
1636 if (FAILED(rc)) throw rc;
1637 /* Clone the source disk image */
1638 rc = srcHdVBox->CloneTo(dstHdVBox, HardDiskVariant_Standard, NULL, pProgress2.asOutParam());
1639 if (FAILED(rc)) throw rc;
1640
1641 /* Advance to the next operation */
1642 if (!pTask->progress.isNull())
1643 pTask->progress->setNextOperation(BstrFmt(tr("Importing virtual disk image '%s'"), strSrcFilePath.c_str()),
1644 vsdeHD->ulSizeMB); // operation's weight, as set up with the IProgress originally);
1645 }
1646
1647 // now wait for the background disk operation to complete; this throws HRESULTs on error
1648 waitForAsyncProgress(pTask->progress, pProgress2);
1649
1650 if (fSourceHdNeedsClosing)
1651 {
1652 rc = srcHdVBox->Close();
1653 if (FAILED(rc)) throw rc;
1654 fSourceHdNeedsClosing = false;
1655 }
1656
1657 llHardDisksCreated.push_back(dstHdVBox);
1658 /* Now use the new uuid to attach the disk image to our new machine */
1659 ComPtr<IMachine> sMachine;
1660 rc = session->COMGETTER(Machine)(sMachine.asOutParam());
1661 if (FAILED(rc)) throw rc;
1662 Bstr hdId;
1663 rc = dstHdVBox->COMGETTER(Id)(hdId.asOutParam());
1664 if (FAILED(rc)) throw rc;
1665
1666 /* For now we assume we have one controller of every type only */
1667 HardDiskController hdc = (*vsysThis.mapControllers.find(vd.idController)).second;
1668
1669 // this is for rollback later
1670 MyHardDiskAttachment mhda;
1671 mhda.uuid = newMachineId;
1672 mhda.pMachine = pNewMachine;
1673
1674 switch (hdc.system)
1675 {
1676 case HardDiskController::IDE:
1677 // For the IDE bus, the channel parameter can be either 0 or 1, to specify the primary
1678 // or secondary IDE controller, respectively. For the primary controller of the IDE bus,
1679 // the device number can be either 0 or 1, to specify the master or the slave device,
1680 // respectively. For the secondary IDE controller, the device number is always 1 because
1681 // the master device is reserved for the CD-ROM drive.
1682 mhda.controllerType = Bstr("IDE");
1683 switch (vd.ulAddressOnParent)
1684 {
1685 case 0: // interpret this as primary master
1686 mhda.lChannel = (long)0;
1687 mhda.lDevice = (long)0;
1688 break;
1689
1690 case 1: // interpret this as primary slave
1691 mhda.lChannel = (long)0;
1692 mhda.lDevice = (long)1;
1693 break;
1694
1695 case 2: // interpret this as secondary slave
1696 mhda.lChannel = (long)1;
1697 mhda.lDevice = (long)1;
1698 break;
1699
1700 default:
1701 throw setError(VBOX_E_NOT_SUPPORTED,
1702 tr("Invalid channel %RI16 specified; IDE controllers support only 0, 1 or 2"), vd.ulAddressOnParent);
1703 break;
1704 }
1705 break;
1706
1707 case HardDiskController::SATA:
1708 mhda.controllerType = Bstr("SATA");
1709 mhda.lChannel = (long)vd.ulAddressOnParent;
1710 mhda.lDevice = (long)0;
1711 break;
1712
1713 case HardDiskController::SCSI:
1714 mhda.controllerType = Bstr("SCSI");
1715 mhda.lChannel = (long)vd.ulAddressOnParent;
1716 mhda.lDevice = (long)0;
1717 break;
1718
1719 default: break;
1720 }
1721
1722 Log(("Attaching disk %s to channel %d on device %d\n", pcszDstFilePath, mhda.lChannel, mhda.lDevice));
1723
1724 rc = sMachine->AttachHardDisk(hdId,
1725 mhda.controllerType,
1726 mhda.lChannel,
1727 mhda.lDevice);
1728 if (FAILED(rc)) throw rc;
1729
1730 llHardDiskAttachments.push_back(mhda);
1731
1732 rc = sMachine->SaveSettings();
1733 if (FAILED(rc)) throw rc;
1734 } // end for (itHD = avsdeHDs.begin();
1735
1736 // only now that we're done with all disks, close the session
1737 rc = session->Close();
1738 if (FAILED(rc)) throw rc;
1739 fSessionOpen = false;
1740 }
1741 catch(HRESULT /* aRC */)
1742 {
1743 if (fSourceHdNeedsClosing)
1744 srcHdVBox->Close();
1745
1746 if (fSessionOpen)
1747 session->Close();
1748
1749 throw;
1750 }
1751 }
1752 }
1753 catch(HRESULT aRC)
1754 {
1755 rc = aRC;
1756 }
1757
1758 if (FAILED(rc))
1759 break;
1760
1761 } // for (it = pAppliance->m->llVirtualSystems.begin(),
1762
1763 if (FAILED(rc))
1764 {
1765 // with _whatever_ error we've had, do a complete roll-back of
1766 // machines and disks we've created; unfortunately this is
1767 // not so trivially done...
1768
1769 HRESULT rc2;
1770 // detach all hard disks from all machines we created
1771 list<MyHardDiskAttachment>::iterator itM;
1772 for (itM = llHardDiskAttachments.begin();
1773 itM != llHardDiskAttachments.end();
1774 ++itM)
1775 {
1776 const MyHardDiskAttachment &mhda = *itM;
1777 rc2 = mVirtualBox->OpenSession(session, Bstr(mhda.uuid));
1778 if (SUCCEEDED(rc2))
1779 {
1780 ComPtr<IMachine> sMachine;
1781 rc2 = session->COMGETTER(Machine)(sMachine.asOutParam());
1782 if (SUCCEEDED(rc2))
1783 {
1784 rc2 = sMachine->DetachHardDisk(Bstr(mhda.controllerType), mhda.lChannel, mhda.lDevice);
1785 rc2 = sMachine->SaveSettings();
1786 }
1787 session->Close();
1788 }
1789 }
1790
1791 // now clean up all hard disks we created
1792 list< ComPtr<IHardDisk> >::iterator itHD;
1793 for (itHD = llHardDisksCreated.begin();
1794 itHD != llHardDisksCreated.end();
1795 ++itHD)
1796 {
1797 ComPtr<IHardDisk> pDisk = *itHD;
1798 ComPtr<IProgress> pProgress;
1799 rc2 = pDisk->DeleteStorage(pProgress.asOutParam());
1800 rc2 = pProgress->WaitForCompletion(-1);
1801 }
1802
1803 // finally, deregister and remove all machines
1804 list<Guid>::iterator itID;
1805 for (itID = llMachinesRegistered.begin();
1806 itID != llMachinesRegistered.end();
1807 ++itID)
1808 {
1809 const Guid &guid = *itID;
1810 ComPtr<IMachine> failedMachine;
1811 rc2 = mVirtualBox->UnregisterMachine(guid.toUtf16(), failedMachine.asOutParam());
1812 if (SUCCEEDED(rc2))
1813 rc2 = failedMachine->DeleteSettings();
1814 }
1815 }
1816
1817 pTask->rc = rc;
1818
1819 if (!pTask->progress.isNull())
1820 pTask->progress->notifyComplete(rc);
1821
1822 LogFlowFunc(("rc=%Rhrc\n", rc));
1823 LogFlowFuncLeave();
1824
1825 return VINF_SUCCESS;
1826}
1827
1828int Appliance::importS3(TaskImportOVF *pTask)
1829{
1830 LogFlowFuncEnter();
1831 LogFlowFunc(("Appliance %p\n", this));
1832
1833 AutoCaller autoCaller(this);
1834 CheckComRCReturnRC(autoCaller.rc());
1835
1836 AutoWriteLock appLock(this);
1837
1838 int vrc = VINF_SUCCESS;
1839 RTS3 hS3 = NIL_RTS3;
1840 char szOSTmpDir[RTPATH_MAX];
1841 RTPathTemp(szOSTmpDir, sizeof(szOSTmpDir));
1842 /* The template for the temporary directory created below */
1843 char *pszTmpDir;
1844 RTStrAPrintf(&pszTmpDir, "%s"RTPATH_SLASH_STR"vbox-ovf-XXXXXX", szOSTmpDir);
1845 list< pair<Utf8Str, ULONG> > filesList;
1846
1847 HRESULT rc = S_OK;
1848 try
1849 {
1850 /* Extract the bucket */
1851 Utf8Str tmpPath = pTask->locInfo.strPath;
1852 Utf8Str bucket;
1853 parseBucket(tmpPath, bucket);
1854
1855 /* We need a temporary directory which we can put the all disk images
1856 * in */
1857 vrc = RTDirCreateTemp(pszTmpDir);
1858 if (RT_FAILURE(rc))
1859 throw setError(VBOX_E_FILE_ERROR,
1860 tr("Cannot create temporary directory '%s'"), pszTmpDir);
1861
1862 /* Add every disks of every virtual system to an internal list */
1863 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;
1864 for (it = m->virtualSystemDescriptions.begin();
1865 it != m->virtualSystemDescriptions.end();
1866 ++it)
1867 {
1868 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);
1869 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
1870 std::list<VirtualSystemDescriptionEntry*>::const_iterator itH;
1871 for (itH = avsdeHDs.begin();
1872 itH != avsdeHDs.end();
1873 ++itH)
1874 {
1875 const Utf8Str &strTargetFile = (*itH)->strOvf;
1876 if (!strTargetFile.isEmpty())
1877 {
1878 /* The temporary name of the target disk file */
1879 Utf8StrFmt strTmpDisk("%s/%s", pszTmpDir, RTPathFilename(strTargetFile));
1880 filesList.push_back(pair<Utf8Str, ULONG>(strTmpDisk, (*itH)->ulSizeMB));
1881 }
1882 }
1883 }
1884
1885 /* Next we have to download the disk images */
1886 vrc = RTS3Create(&hS3, pTask->locInfo.strUsername.c_str(), pTask->locInfo.strPassword.c_str(), pTask->locInfo.strHostname.c_str(), "virtualbox-agent/"VBOX_VERSION_STRING);
1887 if(RT_FAILURE(vrc))
1888 throw setError(VBOX_E_IPRT_ERROR,
1889 tr("Cannot create S3 service handler"));
1890 RTS3SetProgressCallback(hS3, pTask->updateProgress, &pTask);
1891
1892 /* Download all files */
1893 for (list< pair<Utf8Str, ULONG> >::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1)
1894 {
1895 const pair<Utf8Str, ULONG> &s = (*it1);
1896 const Utf8Str &strSrcFile = s.first;
1897 /* Construct the source file name */
1898 char *pszFilename = RTPathFilename(strSrcFile.c_str());
1899 /* Advance to the next operation */
1900 if (!pTask->progress.isNull())
1901 pTask->progress->setNextOperation(BstrFmt(tr("Downloading file '%s'"), pszFilename), s.second);
1902
1903 vrc = RTS3GetKey(hS3, bucket.c_str(), pszFilename, strSrcFile.c_str());
1904 if (RT_FAILURE(vrc))
1905 {
1906 if(vrc == VERR_S3_CANCELED)
1907 throw S_OK; /* todo: !!!!!!!!!!!!! */
1908 else if(vrc == VERR_S3_ACCESS_DENIED)
1909 throw setError(E_ACCESSDENIED,
1910 tr("Cannot download file '%s' from S3 storage server (Access denied)"), pszFilename);
1911 else if(vrc == VERR_S3_NOT_FOUND)
1912 throw setError(VBOX_E_FILE_ERROR,
1913 tr("Cannot download file '%s' from S3 storage server (File not found)"), pszFilename);
1914 else
1915 throw setError(VBOX_E_IPRT_ERROR,
1916 tr("Cannot download file '%s' from S3 storage server (%Rrc)"), pszFilename, vrc);
1917 }
1918 }
1919
1920 /* Provide a OVF file (haven't to exist) so the import routine can
1921 * figure out where the disk images/manifest file are located. */
1922 Utf8StrFmt strTmpOvf("%s/%s", pszTmpDir, RTPathFilename(tmpPath));
1923 /* Now check if there is an manifest file. This is optional. */
1924 Utf8Str strManifestFile = manifestFileName(strTmpOvf);
1925 char *pszFilename = RTPathFilename(strManifestFile.c_str());
1926 if (!pTask->progress.isNull())
1927 pTask->progress->setNextOperation(BstrFmt(tr("Downloading file '%s'"), pszFilename), 1);
1928
1929 /* Try to download it. If the error is VERR_S3_NOT_FOUND, it isn't fatal. */
1930 vrc = RTS3GetKey(hS3, bucket.c_str(), pszFilename, strManifestFile.c_str());
1931 if (RT_SUCCESS(vrc))
1932 filesList.push_back(pair<Utf8Str, ULONG>(strManifestFile, 0));
1933 else if (RT_FAILURE(vrc))
1934 {
1935 if(vrc == VERR_S3_CANCELED)
1936 throw S_OK; /* todo: !!!!!!!!!!!!! */
1937 else if(vrc == VERR_S3_NOT_FOUND)
1938 vrc = VINF_SUCCESS; /* Not found is ok */
1939 else if(vrc == VERR_S3_ACCESS_DENIED)
1940 throw setError(E_ACCESSDENIED,
1941 tr("Cannot download file '%s' from S3 storage server (Access denied)"), pszFilename);
1942 else
1943 throw setError(VBOX_E_IPRT_ERROR,
1944 tr("Cannot download file '%s' from S3 storage server (%Rrc)"), pszFilename, vrc);
1945 }
1946
1947 /* Close the connection early */
1948 RTS3Destroy(hS3);
1949 hS3 = NIL_RTS3;
1950
1951 if (!pTask->progress.isNull())
1952 pTask->progress->setNextOperation(BstrFmt(tr("Importing appliance")), m->ulWeightPerOperation);
1953
1954 ComObjPtr<Progress> progress;
1955 /* Import the whole temporary OVF & the disk images */
1956 LocationInfo li;
1957 li.strPath = strTmpOvf;
1958 rc = importImpl(li, progress);
1959 if (FAILED(rc)) throw rc;
1960
1961 /* Unlock the appliance for the fs import thread */
1962 appLock.unlock();
1963 /* Wait until the import is done, but report the progress back to the
1964 caller */
1965 ComPtr<IProgress> progressInt(progress);
1966 waitForAsyncProgress(pTask->progress, progressInt); /* Any errors will be thrown */
1967
1968 /* Again lock the appliance for the next steps */
1969 appLock.lock();
1970 }
1971 catch(HRESULT aRC)
1972 {
1973 rc = aRC;
1974 }
1975 /* Cleanup */
1976 RTS3Destroy(hS3);
1977 /* Delete all files which where temporary created */
1978 for (list< pair<Utf8Str, ULONG> >::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1)
1979 {
1980 const char *pszFilePath = (*it1).first.c_str();
1981 if (RTPathExists(pszFilePath))
1982 {
1983 vrc = RTFileDelete(pszFilePath);
1984 if(RT_FAILURE(vrc))
1985 rc = setError(VBOX_E_FILE_ERROR,
1986 tr("Cannot delete file '%s' (%Rrc)"), pszFilePath, vrc);
1987 }
1988 }
1989 /* Delete the temporary directory */
1990 if (RTPathExists(pszTmpDir))
1991 {
1992 vrc = RTDirRemove(pszTmpDir);
1993 if(RT_FAILURE(vrc))
1994 rc = setError(VBOX_E_FILE_ERROR,
1995 tr("Cannot delete temporary directory '%s' (%Rrc)"), pszTmpDir, vrc);
1996 }
1997 if (pszTmpDir)
1998 RTStrFree(pszTmpDir);
1999
2000 pTask->rc = rc;
2001
2002 if (!pTask->progress.isNull())
2003 pTask->progress->notifyComplete(rc);
2004
2005 LogFlowFunc(("rc=%Rhrc\n", rc));
2006 LogFlowFuncLeave();
2007
2008 return VINF_SUCCESS;
2009}
2010
2011HRESULT Appliance::writeImpl(int aFormat, const LocationInfo &aLocInfo, ComObjPtr<Progress> &aProgress)
2012{
2013 HRESULT rc = S_OK;
2014 try
2015 {
2016 /* Initialize our worker task */
2017 std::auto_ptr<TaskExportOVF> task(new TaskExportOVF(this));
2018 /* What should the task do */
2019 task->taskType = TaskExportOVF::Write;
2020 /* The OVF version to write */
2021 task->enFormat = (TaskExportOVF::OVFFormat)aFormat;
2022 /* Copy the current location info to the task */
2023 task->locInfo = aLocInfo;
2024
2025 Bstr progressDesc = BstrFmt(tr("Export appliance '%s'"),
2026 task->locInfo.strPath.c_str());
2027
2028 /* todo: This progress init stuff should be done a little bit more generic */
2029 if (task->locInfo.storageType == VFSType_File)
2030 rc = setUpProgressFS(aProgress, progressDesc);
2031 else
2032 rc = setUpProgressWriteS3(aProgress, progressDesc);
2033
2034 task->progress = aProgress;
2035
2036 rc = task->startThread();
2037 CheckComRCThrowRC(rc);
2038
2039 /* Don't destruct on success */
2040 task.release();
2041 }
2042 catch (HRESULT aRC)
2043 {
2044 rc = aRC;
2045 }
2046
2047 return rc;
2048}
2049
2050DECLCALLBACK(int) Appliance::taskThreadWriteOVF(RTTHREAD /* aThread */, void *pvUser)
2051{
2052 std::auto_ptr<TaskExportOVF> task(static_cast<TaskExportOVF*>(pvUser));
2053 AssertReturn(task.get(), VERR_GENERAL_FAILURE);
2054
2055 Appliance *pAppliance = task->pAppliance;
2056
2057 LogFlowFuncEnter();
2058 LogFlowFunc(("Appliance %p\n", pAppliance));
2059
2060 HRESULT rc = S_OK;
2061
2062 switch(task->taskType)
2063 {
2064 case TaskExportOVF::Write:
2065 {
2066 if (task->locInfo.storageType == VFSType_File)
2067 rc = pAppliance->writeFS(task.get());
2068 else if (task->locInfo.storageType == VFSType_S3)
2069 rc = pAppliance->writeS3(task.get());
2070 break;
2071 }
2072 }
2073
2074 LogFlowFunc(("rc=%Rhrc\n", rc));
2075 LogFlowFuncLeave();
2076
2077 return VINF_SUCCESS;
2078}
2079
2080int Appliance::TaskExportOVF::startThread()
2081{
2082 int vrc = RTThreadCreate(NULL, Appliance::taskThreadWriteOVF, this,
2083 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
2084 "Appliance::Task");
2085
2086 ComAssertMsgRCRet(vrc,
2087 ("Could not create taskThreadWriteOVF (%Rrc)\n", vrc), E_FAIL);
2088
2089 return S_OK;
2090}
2091
2092int Appliance::writeFS(TaskExportOVF *pTask)
2093{
2094 LogFlowFuncEnter();
2095 LogFlowFunc(("Appliance %p\n", this));
2096
2097 AutoCaller autoCaller(this);
2098 CheckComRCReturnRC(autoCaller.rc());
2099
2100 AutoWriteLock appLock(this);
2101
2102 HRESULT rc = S_OK;
2103
2104 try
2105 {
2106 xml::Document doc;
2107 xml::ElementNode *pelmRoot = doc.createRootElement("Envelope");
2108
2109 pelmRoot->setAttribute("ovf:version", (pTask->enFormat == TaskExportOVF::OVF_1_0) ? "1.0" : "0.9");
2110 pelmRoot->setAttribute("xml:lang", "en-US");
2111
2112 Utf8Str strNamespace = (pTask->enFormat == TaskExportOVF::OVF_0_9)
2113 ? "http://www.vmware.com/schema/ovf/1/envelope" // 0.9
2114 : "http://schemas.dmtf.org/ovf/envelope/1"; // 1.0
2115 pelmRoot->setAttribute("xmlns", strNamespace);
2116 pelmRoot->setAttribute("xmlns:ovf", strNamespace);
2117
2118// pelmRoot->setAttribute("xmlns:ovfstr", "http://schema.dmtf.org/ovf/strings/1");
2119 pelmRoot->setAttribute("xmlns:rasd", "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData");
2120 pelmRoot->setAttribute("xmlns:vssd", "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData");
2121 pelmRoot->setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
2122// pelmRoot->setAttribute("xsi:schemaLocation", "http://schemas.dmtf.org/ovf/envelope/1 ../ovf-envelope.xsd");
2123
2124 // <Envelope>/<References>
2125 xml::ElementNode *pelmReferences = pelmRoot->createChild("References"); // 0.9 and 1.0
2126
2127 /* <Envelope>/<DiskSection>:
2128 <DiskSection>
2129 <Info>List of the virtual disks used in the package</Info>
2130 <Disk ovf:capacity="4294967296" ovf:diskId="lamp" ovf:format="http://www.vmware.com/specifications/vmdk.html#compressed" ovf:populatedSize="1924967692"/>
2131 </DiskSection> */
2132 xml::ElementNode *pelmDiskSection;
2133 if (pTask->enFormat == TaskExportOVF::OVF_0_9)
2134 {
2135 // <Section xsi:type="ovf:DiskSection_Type">
2136 pelmDiskSection = pelmRoot->createChild("Section");
2137 pelmDiskSection->setAttribute("xsi:type", "ovf:DiskSection_Type");
2138 }
2139 else
2140 pelmDiskSection = pelmRoot->createChild("DiskSection");
2141
2142 xml::ElementNode *pelmDiskSectionInfo = pelmDiskSection->createChild("Info");
2143 pelmDiskSectionInfo->addContent("List of the virtual disks used in the package");
2144 // for now, set up a map so we have a list of unique disk names (to make
2145 // sure the same disk name is only added once)
2146 map<Utf8Str, const VirtualSystemDescriptionEntry*> mapDisks;
2147
2148 /* <Envelope>/<NetworkSection>:
2149 <NetworkSection>
2150 <Info>Logical networks used in the package</Info>
2151 <Network ovf:name="VM Network">
2152 <Description>The network that the LAMP Service will be available on</Description>
2153 </Network>
2154 </NetworkSection> */
2155 xml::ElementNode *pelmNetworkSection;
2156 if (pTask->enFormat == TaskExportOVF::OVF_0_9)
2157 {
2158 // <Section xsi:type="ovf:NetworkSection_Type">
2159 pelmNetworkSection = pelmRoot->createChild("Section");
2160 pelmNetworkSection->setAttribute("xsi:type", "ovf:NetworkSection_Type");
2161 }
2162 else
2163 pelmNetworkSection = pelmRoot->createChild("NetworkSection");
2164
2165 xml::ElementNode *pelmNetworkSectionInfo = pelmNetworkSection->createChild("Info");
2166 pelmNetworkSectionInfo->addContent("Logical networks used in the package");
2167 // for now, set up a map so we have a list of unique network names (to make
2168 // sure the same network name is only added once)
2169 map<Utf8Str, bool> mapNetworks;
2170 // we fill this later below when we iterate over the networks
2171
2172 // and here come the virtual systems:
2173
2174 // write a collection if we have more than one virtual system _and_ we're
2175 // writing OVF 1.0; otherwise fail since ovftool can't import more than
2176 // one machine, it seems
2177 xml::ElementNode *pelmToAddVirtualSystemsTo;
2178 if (m->virtualSystemDescriptions.size() > 1)
2179 {
2180 if (pTask->enFormat == TaskExportOVF::OVF_0_9)
2181 throw setError(VBOX_E_FILE_ERROR,
2182 tr("Cannot export more than one virtual system with OVF 0.9, use OVF 1.0"));
2183
2184 pelmToAddVirtualSystemsTo = pelmRoot->createChild("VirtualSystemCollection");
2185 /* xml::AttributeNode *pattrVirtualSystemCollectionId = */ pelmToAddVirtualSystemsTo->setAttribute("ovf:name", "ExportedVirtualBoxMachines"); // whatever
2186 }
2187 else
2188 pelmToAddVirtualSystemsTo = pelmRoot; // add virtual system directly under root element
2189
2190 uint32_t cDisks = 0;
2191
2192 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;
2193 /* Iterate through all virtual systems of that appliance */
2194 for (it = m->virtualSystemDescriptions.begin();
2195 it != m->virtualSystemDescriptions.end();
2196 ++it)
2197 {
2198 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);
2199
2200 xml::ElementNode *pelmVirtualSystem;
2201 if (pTask->enFormat == TaskExportOVF::OVF_0_9)
2202 {
2203 // <Section xsi:type="ovf:NetworkSection_Type">
2204 pelmVirtualSystem = pelmToAddVirtualSystemsTo->createChild("Content");
2205 pelmVirtualSystem->setAttribute("xsi:type", "ovf:VirtualSystem_Type");
2206 }
2207 else
2208 pelmVirtualSystem = pelmToAddVirtualSystemsTo->createChild("VirtualSystem");
2209
2210 /*xml::ElementNode *pelmVirtualSystemInfo =*/ pelmVirtualSystem->createChild("Info")->addContent("A virtual machine");
2211
2212 std::list<VirtualSystemDescriptionEntry*> llName = vsdescThis->findByType(VirtualSystemDescriptionType_Name);
2213 if (llName.size() != 1)
2214 throw setError(VBOX_E_NOT_SUPPORTED,
2215 tr("Missing VM name"));
2216 Utf8Str &strVMName = llName.front()->strVbox;
2217 pelmVirtualSystem->setAttribute("ovf:id", strVMName);
2218
2219 // product info
2220 std::list<VirtualSystemDescriptionEntry*> llProduct = vsdescThis->findByType(VirtualSystemDescriptionType_Product);
2221 std::list<VirtualSystemDescriptionEntry*> llProductUrl = vsdescThis->findByType(VirtualSystemDescriptionType_ProductUrl);
2222 std::list<VirtualSystemDescriptionEntry*> llVendor = vsdescThis->findByType(VirtualSystemDescriptionType_Vendor);
2223 std::list<VirtualSystemDescriptionEntry*> llVendorUrl = vsdescThis->findByType(VirtualSystemDescriptionType_VendorUrl);
2224 std::list<VirtualSystemDescriptionEntry*> llVersion = vsdescThis->findByType(VirtualSystemDescriptionType_Version);
2225 bool fProduct = llProduct.size() && !llProduct.front()->strVbox.isEmpty();
2226 bool fProductUrl = llProductUrl.size() && !llProductUrl.front()->strVbox.isEmpty();
2227 bool fVendor = llVendor.size() && !llVendor.front()->strVbox.isEmpty();
2228 bool fVendorUrl = llVendorUrl.size() && !llVendorUrl.front()->strVbox.isEmpty();
2229 bool fVersion = llVersion.size() && !llVersion.front()->strVbox.isEmpty();
2230 if (fProduct ||
2231 fProductUrl ||
2232 fVersion ||
2233 fVendorUrl ||
2234 fVersion)
2235 {
2236 /* <Section ovf:required="false" xsi:type="ovf:ProductSection_Type">
2237 <Info>Meta-information about the installed software</Info>
2238 <Product>VAtest</Product>
2239 <Vendor>SUN Microsystems</Vendor>
2240 <Version>10.0</Version>
2241 <ProductUrl>http://blogs.sun.com/VirtualGuru</ProductUrl>
2242 <VendorUrl>http://www.sun.com</VendorUrl>
2243 </Section> */
2244 xml::ElementNode *pelmAnnotationSection;
2245 if (pTask->enFormat == TaskExportOVF::OVF_0_9)
2246 {
2247 // <Section ovf:required="false" xsi:type="ovf:ProductSection_Type">
2248 pelmAnnotationSection = pelmVirtualSystem->createChild("Section");
2249 pelmAnnotationSection->setAttribute("xsi:type", "ovf:ProductSection_Type");
2250 }
2251 else
2252 pelmAnnotationSection = pelmVirtualSystem->createChild("ProductSection");
2253
2254 pelmAnnotationSection->createChild("Info")->addContent("Meta-information about the installed software");
2255 if (fProduct)
2256 pelmAnnotationSection->createChild("Product")->addContent(llProduct.front()->strVbox);
2257 if (fVendor)
2258 pelmAnnotationSection->createChild("Vendor")->addContent(llVendor.front()->strVbox);
2259 if (fVersion)
2260 pelmAnnotationSection->createChild("Version")->addContent(llVersion.front()->strVbox);
2261 if (fProductUrl)
2262 pelmAnnotationSection->createChild("ProductUrl")->addContent(llProductUrl.front()->strVbox);
2263 if (fVendorUrl)
2264 pelmAnnotationSection->createChild("VendorUrl")->addContent(llVendorUrl.front()->strVbox);
2265 }
2266
2267 // description
2268 std::list<VirtualSystemDescriptionEntry*> llDescription = vsdescThis->findByType(VirtualSystemDescriptionType_Description);
2269 if (llDescription.size() &&
2270 !llDescription.front()->strVbox.isEmpty())
2271 {
2272 /* <Section ovf:required="false" xsi:type="ovf:AnnotationSection_Type">
2273 <Info>A human-readable annotation</Info>
2274 <Annotation>Plan 9</Annotation>
2275 </Section> */
2276 xml::ElementNode *pelmAnnotationSection;
2277 if (pTask->enFormat == TaskExportOVF::OVF_0_9)
2278 {
2279 // <Section ovf:required="false" xsi:type="ovf:AnnotationSection_Type">
2280 pelmAnnotationSection = pelmVirtualSystem->createChild("Section");
2281 pelmAnnotationSection->setAttribute("xsi:type", "ovf:AnnotationSection_Type");
2282 }
2283 else
2284 pelmAnnotationSection = pelmVirtualSystem->createChild("AnnotationSection");
2285
2286 pelmAnnotationSection->createChild("Info")->addContent("A human-readable annotation");
2287 pelmAnnotationSection->createChild("Annotation")->addContent(llDescription.front()->strVbox);
2288 }
2289
2290 // license
2291 std::list<VirtualSystemDescriptionEntry*> llLicense = vsdescThis->findByType(VirtualSystemDescriptionType_License);
2292 if (llLicense.size() &&
2293 !llLicense.front()->strVbox.isEmpty())
2294 {
2295 /* <EulaSection>
2296 <Info ovf:msgid="6">License agreement for the Virtual System.</Info>
2297 <License ovf:msgid="1">License terms can go in here.</License>
2298 </EulaSection> */
2299 xml::ElementNode *pelmEulaSection;
2300 if (pTask->enFormat == TaskExportOVF::OVF_0_9)
2301 {
2302 pelmEulaSection = pelmVirtualSystem->createChild("Section");
2303 pelmEulaSection->setAttribute("xsi:type", "ovf:EulaSection_Type");
2304 }
2305 else
2306 pelmEulaSection = pelmVirtualSystem->createChild("EulaSection");
2307
2308 pelmEulaSection->createChild("Info")->addContent("License agreement for the virtual system");
2309 pelmEulaSection->createChild("License")->addContent(llLicense.front()->strVbox);
2310 }
2311
2312 // operating system
2313 std::list<VirtualSystemDescriptionEntry*> llOS = vsdescThis->findByType(VirtualSystemDescriptionType_OS);
2314 if (llOS.size() != 1)
2315 throw setError(VBOX_E_NOT_SUPPORTED,
2316 tr("Missing OS type"));
2317 /* <OperatingSystemSection ovf:id="82">
2318 <Info>Guest Operating System</Info>
2319 <Description>Linux 2.6.x</Description>
2320 </OperatingSystemSection> */
2321 xml::ElementNode *pelmOperatingSystemSection;
2322 if (pTask->enFormat == TaskExportOVF::OVF_0_9)
2323 {
2324 pelmOperatingSystemSection = pelmVirtualSystem->createChild("Section");
2325 pelmOperatingSystemSection->setAttribute("xsi:type", "ovf:OperatingSystemSection_Type");
2326 }
2327 else
2328 pelmOperatingSystemSection = pelmVirtualSystem->createChild("OperatingSystemSection");
2329
2330 pelmOperatingSystemSection->setAttribute("ovf:id", llOS.front()->strOvf);
2331 pelmOperatingSystemSection->createChild("Info")->addContent("The kind of installed guest operating system");
2332 Utf8Str strOSDesc;
2333 convertCIMOSType2VBoxOSType(strOSDesc, (CIMOSType_T)llOS.front()->strOvf.toInt32(), "");
2334 pelmOperatingSystemSection->createChild("Description")->addContent(strOSDesc);
2335
2336 // <VirtualHardwareSection ovf:id="hw1" ovf:transport="iso">
2337 xml::ElementNode *pelmVirtualHardwareSection;
2338 if (pTask->enFormat == TaskExportOVF::OVF_0_9)
2339 {
2340 // <Section xsi:type="ovf:VirtualHardwareSection_Type">
2341 pelmVirtualHardwareSection = pelmVirtualSystem->createChild("Section");
2342 pelmVirtualHardwareSection->setAttribute("xsi:type", "ovf:VirtualHardwareSection_Type");
2343 }
2344 else
2345 pelmVirtualHardwareSection = pelmVirtualSystem->createChild("VirtualHardwareSection");
2346
2347 pelmVirtualHardwareSection->createChild("Info")->addContent("Virtual hardware requirements for a virtual machine");
2348
2349 /* <System>
2350 <vssd:Description>Description of the virtual hardware section.</vssd:Description>
2351 <vssd:ElementName>vmware</vssd:ElementName>
2352 <vssd:InstanceID>1</vssd:InstanceID>
2353 <vssd:VirtualSystemIdentifier>MyLampService</vssd:VirtualSystemIdentifier>
2354 <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType>
2355 </System> */
2356 xml::ElementNode *pelmSystem = pelmVirtualHardwareSection->createChild("System");
2357
2358 pelmSystem->createChild("vssd:ElementName")->addContent("Virtual Hardware Family"); // required OVF 1.0
2359
2360 // <vssd:InstanceId>0</vssd:InstanceId>
2361 if (pTask->enFormat == TaskExportOVF::OVF_0_9)
2362 pelmSystem->createChild("vssd:InstanceId")->addContent("0");
2363 else // capitalization changed...
2364 pelmSystem->createChild("vssd:InstanceID")->addContent("0");
2365
2366 // <vssd:VirtualSystemIdentifier>VAtest</vssd:VirtualSystemIdentifier>
2367 pelmSystem->createChild("vssd:VirtualSystemIdentifier")->addContent(strVMName);
2368 // <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType>
2369 const char *pcszHardware = "virtualbox-2.2";
2370 if (pTask->enFormat == TaskExportOVF::OVF_0_9)
2371 // pretend to be vmware compatible then
2372 pcszHardware = "vmx-6";
2373 pelmSystem->createChild("vssd:VirtualSystemType")->addContent(pcszHardware);
2374
2375 // loop thru all description entries twice; once to write out all
2376 // devices _except_ disk images, and a second time to assign the
2377 // disk images; this is because disk images need to reference
2378 // IDE controllers, and we can't know their instance IDs without
2379 // assigning them first
2380
2381 uint32_t idIDEController = 0;
2382 int32_t lIDEControllerIndex = 0;
2383 uint32_t idSATAController = 0;
2384 int32_t lSATAControllerIndex = 0;
2385 uint32_t idSCSIController = 0;
2386 int32_t lSCSIControllerIndex = 0;
2387
2388 uint32_t ulInstanceID = 1;
2389
2390 for (size_t uLoop = 1;
2391 uLoop <= 2;
2392 ++uLoop)
2393 {
2394 int32_t lIndexThis = 0;
2395 list<VirtualSystemDescriptionEntry>::const_iterator itD;
2396 for (itD = vsdescThis->m->llDescriptions.begin();
2397 itD != vsdescThis->m->llDescriptions.end();
2398 ++itD, ++lIndexThis)
2399 {
2400 const VirtualSystemDescriptionEntry &desc = *itD;
2401
2402 OVFResourceType_T type = (OVFResourceType_T)0; // if this becomes != 0 then we do stuff
2403 Utf8Str strResourceSubType;
2404
2405 Utf8Str strDescription; // results in <rasd:Description>...</rasd:Description> block
2406 Utf8Str strCaption; // results in <rasd:Caption>...</rasd:Caption> block
2407
2408 uint32_t ulParent = 0;
2409
2410 int32_t lVirtualQuantity = -1;
2411 Utf8Str strAllocationUnits;
2412
2413 int32_t lAddress = -1;
2414 int32_t lBusNumber = -1;
2415 int32_t lAddressOnParent = -1;
2416
2417 int32_t lAutomaticAllocation = -1; // 0 means "false", 1 means "true"
2418 Utf8Str strConnection; // results in <rasd:Connection>...</rasd:Connection> block
2419 Utf8Str strHostResource;
2420
2421 uint64_t uTemp;
2422
2423 switch (desc.type)
2424 {
2425 case VirtualSystemDescriptionType_CPU:
2426 /* <Item>
2427 <rasd:Caption>1 virtual CPU</rasd:Caption>
2428 <rasd:Description>Number of virtual CPUs</rasd:Description>
2429 <rasd:ElementName>virtual CPU</rasd:ElementName>
2430 <rasd:InstanceID>1</rasd:InstanceID>
2431 <rasd:ResourceType>3</rasd:ResourceType>
2432 <rasd:VirtualQuantity>1</rasd:VirtualQuantity>
2433 </Item> */
2434 if (uLoop == 1)
2435 {
2436 strDescription = "Number of virtual CPUs";
2437 type = OVFResourceType_Processor; // 3
2438 desc.strVbox.toInt(uTemp);
2439 lVirtualQuantity = uTemp;
2440 strCaption = Utf8StrFmt("%d virtual CPU", lVirtualQuantity); // without this ovftool won't eat the item
2441 }
2442 break;
2443
2444 case VirtualSystemDescriptionType_Memory:
2445 /* <Item>
2446 <rasd:AllocationUnits>MegaBytes</rasd:AllocationUnits>
2447 <rasd:Caption>256 MB of memory</rasd:Caption>
2448 <rasd:Description>Memory Size</rasd:Description>
2449 <rasd:ElementName>Memory</rasd:ElementName>
2450 <rasd:InstanceID>2</rasd:InstanceID>
2451 <rasd:ResourceType>4</rasd:ResourceType>
2452 <rasd:VirtualQuantity>256</rasd:VirtualQuantity>
2453 </Item> */
2454 if (uLoop == 1)
2455 {
2456 strDescription = "Memory Size";
2457 type = OVFResourceType_Memory; // 4
2458 desc.strVbox.toInt(uTemp);
2459 lVirtualQuantity = (int32_t)(uTemp / _1M);
2460 strAllocationUnits = "MegaBytes";
2461 strCaption = Utf8StrFmt("%d MB of memory", lVirtualQuantity); // without this ovftool won't eat the item
2462 }
2463 break;
2464
2465 case VirtualSystemDescriptionType_HardDiskControllerIDE:
2466 /* <Item>
2467 <rasd:Caption>ideController1</rasd:Caption>
2468 <rasd:Description>IDE Controller</rasd:Description>
2469 <rasd:InstanceId>5</rasd:InstanceId>
2470 <rasd:ResourceType>5</rasd:ResourceType>
2471 <rasd:Address>1</rasd:Address>
2472 <rasd:BusNumber>1</rasd:BusNumber>
2473 </Item> */
2474 if (uLoop == 1)
2475 {
2476 strDescription = "IDE Controller";
2477 strCaption = "ideController0";
2478 type = OVFResourceType_IDEController; // 5
2479 strResourceSubType = desc.strVbox;
2480 // it seems that OVFTool always writes these two, and since we can only
2481 // have one IDE controller, we'll use this as well
2482 lAddress = 1;
2483 lBusNumber = 1;
2484
2485 // remember this ID
2486 idIDEController = ulInstanceID;
2487 lIDEControllerIndex = lIndexThis;
2488 }
2489 break;
2490
2491 case VirtualSystemDescriptionType_HardDiskControllerSATA:
2492 /* <Item>
2493 <rasd:Caption>sataController0</rasd:Caption>
2494 <rasd:Description>SATA Controller</rasd:Description>
2495 <rasd:InstanceId>4</rasd:InstanceId>
2496 <rasd:ResourceType>20</rasd:ResourceType>
2497 <rasd:ResourceSubType>ahci</rasd:ResourceSubType>
2498 <rasd:Address>0</rasd:Address>
2499 <rasd:BusNumber>0</rasd:BusNumber>
2500 </Item>
2501 */
2502 if (uLoop == 1)
2503 {
2504 strDescription = "SATA Controller";
2505 strCaption = "sataController0";
2506 type = OVFResourceType_OtherStorageDevice; // 20
2507 // it seems that OVFTool always writes these two, and since we can only
2508 // have one SATA controller, we'll use this as well
2509 lAddress = 0;
2510 lBusNumber = 0;
2511
2512 if ( desc.strVbox.isEmpty() // AHCI is the default in VirtualBox
2513 || (!desc.strVbox.compare("ahci", Utf8Str::CaseInsensitive))
2514 )
2515 strResourceSubType = "AHCI";
2516 else
2517 throw setError(VBOX_E_NOT_SUPPORTED,
2518 tr("Invalid config string \"%s\" in SATA controller"), desc.strVbox.c_str());
2519
2520 // remember this ID
2521 idSATAController = ulInstanceID;
2522 lSATAControllerIndex = lIndexThis;
2523 }
2524 break;
2525
2526 case VirtualSystemDescriptionType_HardDiskControllerSCSI:
2527 /* <Item>
2528 <rasd:Caption>scsiController0</rasd:Caption>
2529 <rasd:Description>SCSI Controller</rasd:Description>
2530 <rasd:InstanceId>4</rasd:InstanceId>
2531 <rasd:ResourceType>6</rasd:ResourceType>
2532 <rasd:ResourceSubType>buslogic</rasd:ResourceSubType>
2533 <rasd:Address>0</rasd:Address>
2534 <rasd:BusNumber>0</rasd:BusNumber>
2535 </Item>
2536 */
2537 if (uLoop == 1)
2538 {
2539 strDescription = "SCSI Controller";
2540 strCaption = "scsiController0";
2541 type = OVFResourceType_ParallelSCSIHBA; // 6
2542 // it seems that OVFTool always writes these two, and since we can only
2543 // have one SATA controller, we'll use this as well
2544 lAddress = 0;
2545 lBusNumber = 0;
2546
2547 if ( desc.strVbox.isEmpty() // LsiLogic is the default in VirtualBox
2548 || (!desc.strVbox.compare("lsilogic", Utf8Str::CaseInsensitive))
2549 )
2550 strResourceSubType = "lsilogic";
2551 else if (!desc.strVbox.compare("buslogic", Utf8Str::CaseInsensitive))
2552 strResourceSubType = "buslogic";
2553 else
2554 throw setError(VBOX_E_NOT_SUPPORTED,
2555 tr("Invalid config string \"%s\" in SCSI controller"), desc.strVbox.c_str());
2556
2557 // remember this ID
2558 idSCSIController = ulInstanceID;
2559 lSCSIControllerIndex = lIndexThis;
2560 }
2561 break;
2562
2563 case VirtualSystemDescriptionType_HardDiskImage:
2564 /* <Item>
2565 <rasd:Caption>disk1</rasd:Caption>
2566 <rasd:InstanceId>8</rasd:InstanceId>
2567 <rasd:ResourceType>17</rasd:ResourceType>
2568 <rasd:HostResource>/disk/vmdisk1</rasd:HostResource>
2569 <rasd:Parent>4</rasd:Parent>
2570 <rasd:AddressOnParent>0</rasd:AddressOnParent>
2571 </Item> */
2572 if (uLoop == 2)
2573 {
2574 Utf8Str strDiskID = Utf8StrFmt("vmdisk%RI32", ++cDisks);
2575
2576 strDescription = "Disk Image";
2577 strCaption = Utf8StrFmt("disk%RI32", cDisks); // this is not used for anything else
2578 type = OVFResourceType_HardDisk; // 17
2579
2580 // the following references the "<Disks>" XML block
2581 strHostResource = Utf8StrFmt("/disk/%s", strDiskID.c_str());
2582
2583 // controller=<index>;channel=<c>
2584 size_t pos1 = desc.strExtraConfig.find("controller=");
2585 size_t pos2 = desc.strExtraConfig.find("channel=");
2586 if (pos1 != Utf8Str::npos)
2587 {
2588 int32_t lControllerIndex = -1;
2589 RTStrToInt32Ex(desc.strExtraConfig.c_str() + pos1 + 11, NULL, 0, &lControllerIndex);
2590 if (lControllerIndex == lIDEControllerIndex)
2591 ulParent = idIDEController;
2592 else if (lControllerIndex == lSCSIControllerIndex)
2593 ulParent = idSCSIController;
2594 else if (lControllerIndex == lSATAControllerIndex)
2595 ulParent = idSATAController;
2596 }
2597 if (pos2 != Utf8Str::npos)
2598 RTStrToInt32Ex(desc.strExtraConfig.c_str() + pos2 + 8, NULL, 0, &lAddressOnParent);
2599
2600 if ( !ulParent
2601 || lAddressOnParent == -1
2602 )
2603 throw setError(VBOX_E_NOT_SUPPORTED,
2604 tr("Missing or bad extra config string in hard disk image: \"%s\""), desc.strExtraConfig.c_str());
2605
2606 mapDisks[strDiskID] = &desc;
2607 }
2608 break;
2609
2610 case VirtualSystemDescriptionType_Floppy:
2611 if (uLoop == 1)
2612 {
2613 strDescription = "Floppy Drive";
2614 strCaption = "floppy0"; // this is what OVFTool writes
2615 type = OVFResourceType_FloppyDrive; // 14
2616 lAutomaticAllocation = 0;
2617 lAddressOnParent = 0; // this is what OVFTool writes
2618 }
2619 break;
2620
2621 case VirtualSystemDescriptionType_CDROM:
2622 if (uLoop == 2)
2623 {
2624 // we can't have a CD without an IDE controller
2625 if (!idIDEController)
2626 throw setError(VBOX_E_NOT_SUPPORTED,
2627 tr("Can't have CD-ROM without IDE controller"));
2628
2629 strDescription = "CD-ROM Drive";
2630 strCaption = "cdrom1"; // this is what OVFTool writes
2631 type = OVFResourceType_CDDrive; // 15
2632 lAutomaticAllocation = 1;
2633 ulParent = idIDEController;
2634 lAddressOnParent = 0; // this is what OVFTool writes
2635 }
2636 break;
2637
2638 case VirtualSystemDescriptionType_NetworkAdapter:
2639 /* <Item>
2640 <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
2641 <rasd:Caption>Ethernet adapter on 'VM Network'</rasd:Caption>
2642 <rasd:Connection>VM Network</rasd:Connection>
2643 <rasd:ElementName>VM network</rasd:ElementName>
2644 <rasd:InstanceID>3</rasd:InstanceID>
2645 <rasd:ResourceType>10</rasd:ResourceType>
2646 </Item> */
2647 if (uLoop == 1)
2648 {
2649 lAutomaticAllocation = 1;
2650 strCaption = Utf8StrFmt("Ethernet adapter on '%s'", desc.strOvf.c_str());
2651 type = OVFResourceType_EthernetAdapter; // 10
2652 /* Set the hardware type to something useful.
2653 * To be compatible with vmware & others we set
2654 * PCNet32 for our PCNet types & E1000 for the
2655 * E1000 cards. */
2656 switch (desc.strVbox.toInt32())
2657 {
2658 case NetworkAdapterType_Am79C970A:
2659 case NetworkAdapterType_Am79C973: strResourceSubType = "PCNet32"; break;
2660#ifdef VBOX_WITH_E1000
2661 case NetworkAdapterType_I82540EM:
2662 case NetworkAdapterType_I82545EM:
2663 case NetworkAdapterType_I82543GC: strResourceSubType = "E1000"; break;
2664#endif /* VBOX_WITH_E1000 */
2665 }
2666 strConnection = desc.strOvf;
2667
2668 mapNetworks[desc.strOvf] = true;
2669 }
2670 break;
2671
2672 case VirtualSystemDescriptionType_USBController:
2673 /* <Item ovf:required="false">
2674 <rasd:Caption>usb</rasd:Caption>
2675 <rasd:Description>USB Controller</rasd:Description>
2676 <rasd:InstanceId>3</rasd:InstanceId>
2677 <rasd:ResourceType>23</rasd:ResourceType>
2678 <rasd:Address>0</rasd:Address>
2679 <rasd:BusNumber>0</rasd:BusNumber>
2680 </Item> */
2681 if (uLoop == 1)
2682 {
2683 strDescription = "USB Controller";
2684 strCaption = "usb";
2685 type = OVFResourceType_USBController; // 23
2686 lAddress = 0; // this is what OVFTool writes
2687 lBusNumber = 0; // this is what OVFTool writes
2688 }
2689 break;
2690
2691 case VirtualSystemDescriptionType_SoundCard:
2692 /* <Item ovf:required="false">
2693 <rasd:Caption>sound</rasd:Caption>
2694 <rasd:Description>Sound Card</rasd:Description>
2695 <rasd:InstanceId>10</rasd:InstanceId>
2696 <rasd:ResourceType>35</rasd:ResourceType>
2697 <rasd:ResourceSubType>ensoniq1371</rasd:ResourceSubType>
2698 <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
2699 <rasd:AddressOnParent>3</rasd:AddressOnParent>
2700 </Item> */
2701 if (uLoop == 1)
2702 {
2703 strDescription = "Sound Card";
2704 strCaption = "sound";
2705 type = OVFResourceType_SoundCard; // 35
2706 strResourceSubType = desc.strOvf; // e.g. ensoniq1371
2707 lAutomaticAllocation = 0;
2708 lAddressOnParent = 3; // what gives? this is what OVFTool writes
2709 }
2710 break;
2711 }
2712
2713 if (type)
2714 {
2715 xml::ElementNode *pItem;
2716
2717 pItem = pelmVirtualHardwareSection->createChild("Item");
2718
2719 // NOTE: do not change the order of these items without good reason! While we don't care
2720 // about ordering, VMware's ovftool does and fails if the items are not written in
2721 // exactly this order, as stupid as it seems.
2722
2723 if (!strCaption.isEmpty())
2724 {
2725 pItem->createChild("rasd:Caption")->addContent(strCaption);
2726 if (pTask->enFormat == TaskExportOVF::OVF_1_0)
2727 pItem->createChild("rasd:ElementName")->addContent(strCaption);
2728 }
2729
2730 if (!strDescription.isEmpty())
2731 pItem->createChild("rasd:Description")->addContent(strDescription);
2732
2733 // <rasd:InstanceID>1</rasd:InstanceID>
2734 xml::ElementNode *pelmInstanceID;
2735 if (pTask->enFormat == TaskExportOVF::OVF_0_9)
2736 pelmInstanceID = pItem->createChild("rasd:InstanceId");
2737 else
2738 pelmInstanceID = pItem->createChild("rasd:InstanceID"); // capitalization changed...
2739 pelmInstanceID->addContent(Utf8StrFmt("%d", ulInstanceID++));
2740
2741 // <rasd:ResourceType>3</rasd:ResourceType>
2742 pItem->createChild("rasd:ResourceType")->addContent(Utf8StrFmt("%d", type));
2743 if (!strResourceSubType.isEmpty())
2744 pItem->createChild("rasd:ResourceSubType")->addContent(strResourceSubType);
2745
2746 if (!strHostResource.isEmpty())
2747 pItem->createChild("rasd:HostResource")->addContent(strHostResource);
2748
2749 if (!strAllocationUnits.isEmpty())
2750 pItem->createChild("rasd:AllocationUnits")->addContent(strAllocationUnits);
2751
2752 // <rasd:VirtualQuantity>1</rasd:VirtualQuantity>
2753 if (lVirtualQuantity != -1)
2754 pItem->createChild("rasd:VirtualQuantity")->addContent(Utf8StrFmt("%d", lVirtualQuantity));
2755
2756 if (lAutomaticAllocation != -1)
2757 pItem->createChild("rasd:AutomaticAllocation")->addContent( (lAutomaticAllocation) ? "true" : "false" );
2758
2759 if (!strConnection.isEmpty())
2760 pItem->createChild("rasd:Connection")->addContent(strConnection);
2761
2762 if (lAddress != -1)
2763 pItem->createChild("rasd:Address")->addContent(Utf8StrFmt("%d", lAddress));
2764
2765 if (lBusNumber != -1)
2766 if (pTask->enFormat == TaskExportOVF::OVF_0_9) // BusNumber is invalid OVF 1.0 so only write it in 0.9 mode for OVFTool compatibility
2767 pItem->createChild("rasd:BusNumber")->addContent(Utf8StrFmt("%d", lBusNumber));
2768
2769 if (ulParent)
2770 pItem->createChild("rasd:Parent")->addContent(Utf8StrFmt("%d", ulParent));
2771 if (lAddressOnParent != -1)
2772 pItem->createChild("rasd:AddressOnParent")->addContent(Utf8StrFmt("%d", lAddressOnParent));
2773 }
2774 }
2775 } // for (size_t uLoop = 0; ...
2776 }
2777
2778 // finally, fill in the network section we set up empty above according
2779 // to the networks we found with the hardware items
2780 map<Utf8Str, bool>::const_iterator itN;
2781 for (itN = mapNetworks.begin();
2782 itN != mapNetworks.end();
2783 ++itN)
2784 {
2785 const Utf8Str &strNetwork = itN->first;
2786 xml::ElementNode *pelmNetwork = pelmNetworkSection->createChild("Network");
2787 pelmNetwork->setAttribute("ovf:name", strNetwork.c_str());
2788 pelmNetwork->createChild("Description")->addContent("Logical network used by this appliance.");
2789 }
2790
2791 list<Utf8Str> diskList;
2792 map<Utf8Str, const VirtualSystemDescriptionEntry*>::const_iterator itS;
2793 uint32_t ulFile = 1;
2794 for (itS = mapDisks.begin();
2795 itS != mapDisks.end();
2796 ++itS)
2797 {
2798 const Utf8Str &strDiskID = itS->first;
2799 const VirtualSystemDescriptionEntry *pDiskEntry = itS->second;
2800
2801 // source path: where the VBox image is
2802 const Utf8Str &strSrcFilePath = pDiskEntry->strVbox;
2803 Bstr bstrSrcFilePath(strSrcFilePath);
2804 if (!RTPathExists(strSrcFilePath.c_str()))
2805 /* This isn't allowed */
2806 throw setError(VBOX_E_FILE_ERROR,
2807 tr("Source virtual disk image file '%s' doesn't exist"),
2808 strSrcFilePath.c_str());
2809
2810 // output filename
2811 const Utf8Str &strTargetFileNameOnly = pDiskEntry->strOvf;
2812 // target path needs to be composed from where the output OVF is
2813 Utf8Str strTargetFilePath(pTask->locInfo.strPath);
2814 strTargetFilePath.stripFilename();
2815 strTargetFilePath.append("/");
2816 strTargetFilePath.append(strTargetFileNameOnly);
2817
2818 // clone the disk:
2819 ComPtr<IHardDisk> pSourceDisk;
2820 ComPtr<IHardDisk> pTargetDisk;
2821 ComPtr<IProgress> pProgress2;
2822
2823 Log(("Finding source disk \"%ls\"\n", bstrSrcFilePath.raw()));
2824 rc = mVirtualBox->FindHardDisk(bstrSrcFilePath, pSourceDisk.asOutParam());
2825 if (FAILED(rc)) throw rc;
2826
2827 /* We are always exporting to vmdfk stream optimized for now */
2828 Bstr bstrSrcFormat = L"VMDK";
2829
2830 // create a new hard disk interface for the destination disk image
2831 Log(("Creating target disk \"%s\"\n", strTargetFilePath.raw()));
2832 rc = mVirtualBox->CreateHardDisk(bstrSrcFormat, Bstr(strTargetFilePath), pTargetDisk.asOutParam());
2833 if (FAILED(rc)) throw rc;
2834
2835 // the target disk is now registered and needs to be removed again,
2836 // both after successful cloning or if anything goes bad!
2837 try
2838 {
2839 // create a flat copy of the source disk image
2840 rc = pSourceDisk->CloneTo(pTargetDisk, HardDiskVariant_VmdkStreamOptimized, NULL, pProgress2.asOutParam());
2841 if (FAILED(rc)) throw rc;
2842
2843 // advance to the next operation
2844 if (!pTask->progress.isNull())
2845 pTask->progress->setNextOperation(BstrFmt(tr("Exporting virtual disk image '%s'"), strSrcFilePath.c_str()),
2846 pDiskEntry->ulSizeMB); // operation's weight, as set up with the IProgress originally);
2847
2848 // now wait for the background disk operation to complete; this throws HRESULTs on error
2849 waitForAsyncProgress(pTask->progress, pProgress2);
2850 }
2851 catch (HRESULT rc3)
2852 {
2853 // upon error after registering, close the disk or
2854 // it'll stick in the registry forever
2855 pTargetDisk->Close();
2856 throw;
2857 }
2858 diskList.push_back(strTargetFilePath);
2859
2860 // we need the following for the XML
2861 uint64_t cbFile = 0; // actual file size
2862 rc = pTargetDisk->COMGETTER(Size)(&cbFile);
2863 if (FAILED(rc)) throw rc;
2864
2865 ULONG64 cbCapacity = 0; // size reported to guest
2866 rc = pTargetDisk->COMGETTER(LogicalSize)(&cbCapacity);
2867 if (FAILED(rc)) throw rc;
2868 // capacity is reported in megabytes, so...
2869 cbCapacity *= _1M;
2870
2871 // upon success, close the disk as well
2872 rc = pTargetDisk->Close();
2873 if (FAILED(rc)) throw rc;
2874
2875 // now handle the XML for the disk:
2876 Utf8StrFmt strFileRef("file%RI32", ulFile++);
2877 // <File ovf:href="WindowsXpProfessional-disk1.vmdk" ovf:id="file1" ovf:size="1710381056"/>
2878 xml::ElementNode *pelmFile = pelmReferences->createChild("File");
2879 pelmFile->setAttribute("ovf:href", strTargetFileNameOnly);
2880 pelmFile->setAttribute("ovf:id", strFileRef);
2881 pelmFile->setAttribute("ovf:size", Utf8StrFmt("%RI64", cbFile).c_str());
2882
2883 // add disk to XML Disks section
2884 // <Disk ovf:capacity="8589934592" ovf:diskId="vmdisk1" ovf:fileRef="file1" ovf:format="http://www.vmware.com/specifications/vmdk.html#sparse"/>
2885 xml::ElementNode *pelmDisk = pelmDiskSection->createChild("Disk");
2886 pelmDisk->setAttribute("ovf:capacity", Utf8StrFmt("%RI64", cbCapacity).c_str());
2887 pelmDisk->setAttribute("ovf:diskId", strDiskID);
2888 pelmDisk->setAttribute("ovf:fileRef", strFileRef);
2889 pelmDisk->setAttribute("ovf:format", "http://www.vmware.com/specifications/vmdk.html#sparse"); // must be sparse or ovftool chokes
2890 }
2891
2892 // now go write the XML
2893 xml::XmlFileWriter writer(doc);
2894 writer.write(pTask->locInfo.strPath.c_str());
2895
2896 /* Create & write the manifest file */
2897 const char** ppManifestFiles = (const char**)RTMemAlloc(sizeof(char*)*diskList.size() + 1);
2898 ppManifestFiles[0] = pTask->locInfo.strPath.c_str();
2899 list<Utf8Str>::const_iterator it1;
2900 size_t i = 1;
2901 for (it1 = diskList.begin();
2902 it1 != diskList.end();
2903 ++it1, ++i)
2904 ppManifestFiles[i] = (*it1).c_str();
2905 Utf8Str strMfFile = manifestFileName(pTask->locInfo.strPath.c_str());
2906 int vrc = RTManifestWriteFiles(strMfFile.c_str(), ppManifestFiles, diskList.size()+1);
2907 if (RT_FAILURE(vrc))
2908 throw setError(VBOX_E_FILE_ERROR,
2909 tr("Couldn't create manifest file '%s' (%Rrc)"),
2910 RTPathFilename(strMfFile.c_str()), vrc);
2911 RTMemFree(ppManifestFiles);
2912 }
2913 catch(xml::Error &x)
2914 {
2915 rc = setError(VBOX_E_FILE_ERROR,
2916 x.what());
2917 }
2918 catch(HRESULT aRC)
2919 {
2920 rc = aRC;
2921 }
2922
2923 pTask->rc = rc;
2924
2925 if (!pTask->progress.isNull())
2926 pTask->progress->notifyComplete(rc);
2927
2928 LogFlowFunc(("rc=%Rhrc\n", rc));
2929 LogFlowFuncLeave();
2930
2931 return VINF_SUCCESS;
2932}
2933
2934int Appliance::writeS3(TaskExportOVF *pTask)
2935{
2936 LogFlowFuncEnter();
2937 LogFlowFunc(("Appliance %p\n", this));
2938
2939 AutoCaller autoCaller(this);
2940 CheckComRCReturnRC(autoCaller.rc());
2941
2942 HRESULT rc = S_OK;
2943
2944 AutoWriteLock appLock(this);
2945
2946 int vrc = VINF_SUCCESS;
2947 RTS3 hS3 = NIL_RTS3;
2948 char szOSTmpDir[RTPATH_MAX];
2949 RTPathTemp(szOSTmpDir, sizeof(szOSTmpDir));
2950 /* The template for the temporary directory created below */
2951 char *pszTmpDir;
2952 RTStrAPrintf(&pszTmpDir, "%s"RTPATH_SLASH_STR"vbox-ovf-XXXXXX", szOSTmpDir);
2953 list< pair<Utf8Str, ULONG> > filesList;
2954
2955 // todo:
2956 // - usable error codes
2957 // - seems snapshot filenames are problematic {uuid}.vdi
2958 try
2959 {
2960 /* Extract the bucket */
2961 Utf8Str tmpPath = pTask->locInfo.strPath;
2962 Utf8Str bucket;
2963 parseBucket(tmpPath, bucket);
2964
2965 /* We need a temporary directory which we can put the OVF file & all
2966 * disk images in */
2967 vrc = RTDirCreateTemp(pszTmpDir);
2968 if (RT_FAILURE(rc))
2969 throw setError(VBOX_E_FILE_ERROR,
2970 tr("Cannot create temporary directory '%s'"), pszTmpDir);
2971
2972 /* The temporary name of the target OVF file */
2973 Utf8StrFmt strTmpOvf("%s/%s", pszTmpDir, RTPathFilename(tmpPath));
2974
2975 /* Prepare the temporary writing of the OVF */
2976 ComObjPtr<Progress> progress;
2977 /* Create a temporary file based location info for the sub task */
2978 LocationInfo li;
2979 li.strPath = strTmpOvf;
2980 rc = writeImpl(pTask->enFormat, li, progress);
2981 if (FAILED(rc)) throw rc;
2982
2983 /* Unlock the appliance for the writing thread */
2984 appLock.unlock();
2985 /* Wait until the writing is done, but report the progress back to the
2986 caller */
2987 ComPtr<IProgress> progressInt(progress);
2988 waitForAsyncProgress(pTask->progress, progressInt); /* Any errors will be thrown */
2989
2990 /* Again lock the appliance for the next steps */
2991 appLock.lock();
2992
2993 vrc = RTPathExists(strTmpOvf.c_str()); /* Paranoid check */
2994 if(RT_FAILURE(vrc))
2995 throw setError(VBOX_E_FILE_ERROR,
2996 tr("Cannot find source file '%s'"), strTmpOvf.c_str());
2997 /* Add the OVF file */
2998 filesList.push_back(pair<Utf8Str, ULONG>(strTmpOvf, m->ulWeightPerOperation)); /* Use 1% of the total for the OVF file upload */
2999 Utf8Str strMfFile = manifestFileName(strTmpOvf);
3000 filesList.push_back(pair<Utf8Str, ULONG>(strMfFile , m->ulWeightPerOperation)); /* Use 1% of the total for the manifest file upload */
3001
3002 /* Now add every disks of every virtual system */
3003 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;
3004 for (it = m->virtualSystemDescriptions.begin();
3005 it != m->virtualSystemDescriptions.end();
3006 ++it)
3007 {
3008 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);
3009 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
3010 std::list<VirtualSystemDescriptionEntry*>::const_iterator itH;
3011 for (itH = avsdeHDs.begin();
3012 itH != avsdeHDs.end();
3013 ++itH)
3014 {
3015 const Utf8Str &strTargetFileNameOnly = (*itH)->strOvf;
3016 /* Target path needs to be composed from where the output OVF is */
3017 Utf8Str strTargetFilePath(strTmpOvf);
3018 strTargetFilePath.stripFilename();
3019 strTargetFilePath.append("/");
3020 strTargetFilePath.append(strTargetFileNameOnly);
3021 vrc = RTPathExists(strTargetFilePath.c_str()); /* Paranoid check */
3022 if(RT_FAILURE(vrc))
3023 throw setError(VBOX_E_FILE_ERROR,
3024 tr("Cannot find source file '%s'"), strTargetFilePath.c_str());
3025 filesList.push_back(pair<Utf8Str, ULONG>(strTargetFilePath, (*itH)->ulSizeMB));
3026 }
3027 }
3028 /* Next we have to upload the OVF & all disk images */
3029 vrc = RTS3Create(&hS3, pTask->locInfo.strUsername.c_str(), pTask->locInfo.strPassword.c_str(), pTask->locInfo.strHostname.c_str(), "virtualbox-agent/"VBOX_VERSION_STRING);
3030 if(RT_FAILURE(vrc))
3031 throw setError(VBOX_E_IPRT_ERROR,
3032 tr("Cannot create S3 service handler"));
3033 RTS3SetProgressCallback(hS3, pTask->updateProgress, &pTask);
3034
3035 /* Upload all files */
3036 for (list< pair<Utf8Str, ULONG> >::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1)
3037 {
3038 const pair<Utf8Str, ULONG> &s = (*it1);
3039 char *pszFilename = RTPathFilename(s.first.c_str());
3040 /* Advance to the next operation */
3041 if (!pTask->progress.isNull())
3042 pTask->progress->setNextOperation(BstrFmt(tr("Uploading file '%s'"), pszFilename), s.second);
3043 vrc = RTS3PutKey(hS3, bucket.c_str(), pszFilename, s.first.c_str());
3044 if (RT_FAILURE(vrc))
3045 {
3046 if(vrc == VERR_S3_CANCELED)
3047 break;
3048 else if(vrc == VERR_S3_ACCESS_DENIED)
3049 throw setError(E_ACCESSDENIED,
3050 tr("Cannot upload file '%s' to S3 storage server (Access denied)"), pszFilename);
3051 else if(vrc == VERR_S3_NOT_FOUND)
3052 throw setError(VBOX_E_FILE_ERROR,
3053 tr("Cannot upload file '%s' to S3 storage server (File not found)"), pszFilename);
3054 else
3055 throw setError(VBOX_E_IPRT_ERROR,
3056 tr("Cannot upload file '%s' to S3 storage server (%Rrc)"), pszFilename, vrc);
3057 }
3058 }
3059 }
3060 catch(HRESULT aRC)
3061 {
3062 rc = aRC;
3063 }
3064 /* Cleanup */
3065 RTS3Destroy(hS3);
3066 /* Delete all files which where temporary created */
3067 for (list< pair<Utf8Str, ULONG> >::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1)
3068 {
3069 const char *pszFilePath = (*it1).first.c_str();
3070 if (RTPathExists(pszFilePath))
3071 {
3072 vrc = RTFileDelete(pszFilePath);
3073 if(RT_FAILURE(vrc))
3074 rc = setError(VBOX_E_FILE_ERROR,
3075 tr("Cannot delete file '%s' (%Rrc)"), pszFilePath, vrc);
3076 }
3077 }
3078 /* Delete the temporary directory */
3079 if (RTPathExists(pszTmpDir))
3080 {
3081 vrc = RTDirRemove(pszTmpDir);
3082 if(RT_FAILURE(vrc))
3083 rc = setError(VBOX_E_FILE_ERROR,
3084 tr("Cannot delete temporary directory '%s' (%Rrc)"), pszTmpDir, vrc);
3085 }
3086 if (pszTmpDir)
3087 RTStrFree(pszTmpDir);
3088
3089 pTask->rc = rc;
3090
3091 if (!pTask->progress.isNull())
3092 pTask->progress->notifyComplete(rc);
3093
3094 LogFlowFunc(("rc=%Rhrc\n", rc));
3095 LogFlowFuncLeave();
3096
3097 return VINF_SUCCESS;
3098}
3099
3100////////////////////////////////////////////////////////////////////////////////
3101//
3102// IAppliance public methods
3103//
3104////////////////////////////////////////////////////////////////////////////////
3105
3106/**
3107 * Public method implementation.
3108 * @param
3109 * @return
3110 */
3111STDMETHODIMP Appliance::COMGETTER(Path)(BSTR *aPath)
3112{
3113 if (!aPath)
3114 return E_POINTER;
3115
3116 AutoCaller autoCaller(this);
3117 CheckComRCReturnRC(autoCaller.rc());
3118
3119 AutoReadLock alock(this);
3120
3121 Bstr bstrPath(m->locInfo.strPath);
3122 bstrPath.cloneTo(aPath);
3123
3124 return S_OK;
3125}
3126
3127/**
3128 * Public method implementation.
3129 * @param
3130 * @return
3131 */
3132STDMETHODIMP Appliance::COMGETTER(Disks)(ComSafeArrayOut(BSTR, aDisks))
3133{
3134 CheckComArgOutSafeArrayPointerValid(aDisks);
3135
3136 AutoCaller autoCaller(this);
3137 CheckComRCReturnRC(autoCaller.rc());
3138
3139 AutoReadLock alock(this);
3140
3141 if (m->pReader) // OVFReader instantiated?
3142 {
3143 size_t c = m->pReader->m_mapDisks.size();
3144 com::SafeArray<BSTR> sfaDisks(c);
3145
3146 DiskImagesMap::const_iterator it;
3147 size_t i = 0;
3148 for (it = m->pReader->m_mapDisks.begin();
3149 it != m->pReader->m_mapDisks.end();
3150 ++it, ++i)
3151 {
3152 // create a string representing this disk
3153 const DiskImage &d = it->second;
3154 char *psz = NULL;
3155 RTStrAPrintf(&psz,
3156 "%s\t"
3157 "%RI64\t"
3158 "%RI64\t"
3159 "%s\t"
3160 "%s\t"
3161 "%RI64\t"
3162 "%RI64\t"
3163 "%s",
3164 d.strDiskId.c_str(),
3165 d.iCapacity,
3166 d.iPopulatedSize,
3167 d.strFormat.c_str(),
3168 d.strHref.c_str(),
3169 d.iSize,
3170 d.iChunkSize,
3171 d.strCompression.c_str());
3172 Utf8Str utf(psz);
3173 Bstr bstr(utf);
3174 // push to safearray
3175 bstr.cloneTo(&sfaDisks[i]);
3176 RTStrFree(psz);
3177 }
3178
3179 sfaDisks.detachTo(ComSafeArrayOutArg(aDisks));
3180 }
3181
3182 return S_OK;
3183}
3184
3185/**
3186 * Public method implementation.
3187 * @param
3188 * @return
3189 */
3190STDMETHODIMP Appliance::COMGETTER(VirtualSystemDescriptions)(ComSafeArrayOut(IVirtualSystemDescription*, aVirtualSystemDescriptions))
3191{
3192 CheckComArgOutSafeArrayPointerValid(aVirtualSystemDescriptions);
3193
3194 AutoCaller autoCaller(this);
3195 CheckComRCReturnRC(autoCaller.rc());
3196
3197 AutoReadLock alock(this);
3198
3199 SafeIfaceArray<IVirtualSystemDescription> sfaVSD(m->virtualSystemDescriptions);
3200 sfaVSD.detachTo(ComSafeArrayOutArg(aVirtualSystemDescriptions));
3201
3202 return S_OK;
3203}
3204
3205/**
3206 * Public method implementation.
3207 * @param path
3208 * @return
3209 */
3210STDMETHODIMP Appliance::Read(IN_BSTR path, IProgress **aProgress)
3211{
3212 if (!path) return E_POINTER;
3213 CheckComArgOutPointerValid(aProgress);
3214
3215 AutoCaller autoCaller(this);
3216 CheckComRCReturnRC(autoCaller.rc());
3217
3218 AutoWriteLock alock(this);
3219
3220 if (m->pReader)
3221 {
3222 delete m->pReader;
3223 m->pReader = NULL;
3224 }
3225
3226 // see if we can handle this file; for now we insist it has an ".ovf" extension
3227 Utf8Str strPath (path);
3228 if (!strPath.endsWith(".ovf", Utf8Str::CaseInsensitive))
3229 return setError(VBOX_E_FILE_ERROR,
3230 tr("Appliance file must have .ovf extension"));
3231
3232 ComObjPtr<Progress> progress;
3233 HRESULT rc = S_OK;
3234 try
3235 {
3236 /* Parse all necessary info out of the URI */
3237 parseURI(strPath, m->locInfo);
3238 rc = readImpl(m->locInfo, progress);
3239 }
3240 catch (HRESULT aRC)
3241 {
3242 rc = aRC;
3243 }
3244
3245 if (SUCCEEDED(rc))
3246 /* Return progress to the caller */
3247 progress.queryInterfaceTo(aProgress);
3248
3249 return S_OK;
3250}
3251
3252/**
3253 * Public method implementation.
3254 * @return
3255 */
3256STDMETHODIMP Appliance::Interpret()
3257{
3258 // @todo:
3259 // - don't use COM methods but the methods directly (faster, but needs appropriate locking of that objects itself (s. HardDisk))
3260 // - Appropriate handle errors like not supported file formats
3261 AutoCaller autoCaller(this);
3262 CheckComRCReturnRC(autoCaller.rc());
3263
3264 AutoWriteLock(this);
3265
3266 HRESULT rc = S_OK;
3267
3268 /* Clear any previous virtual system descriptions */
3269 m->virtualSystemDescriptions.clear();
3270
3271 /* We need the default path for storing disk images */
3272 ComPtr<ISystemProperties> systemProps;
3273 rc = mVirtualBox->COMGETTER(SystemProperties)(systemProps.asOutParam());
3274 CheckComRCReturnRC(rc);
3275 Bstr bstrDefaultHardDiskLocation;
3276 rc = systemProps->COMGETTER(DefaultHardDiskFolder)(bstrDefaultHardDiskLocation.asOutParam());
3277 CheckComRCReturnRC(rc);
3278
3279 if (!m->pReader)
3280 return setError(E_FAIL,
3281 tr("Cannot interpret appliance without reading it first (call read() before interpret())"));
3282
3283 /* Try/catch so we can clean up on error */
3284 try
3285 {
3286 list<VirtualSystem>::const_iterator it;
3287 /* Iterate through all virtual systems */
3288 for (it = m->pReader->m_llVirtualSystems.begin();
3289 it != m->pReader->m_llVirtualSystems.end();
3290 ++it)
3291 {
3292 const VirtualSystem &vsysThis = *it;
3293
3294 ComObjPtr<VirtualSystemDescription> pNewDesc;
3295 rc = pNewDesc.createObject();
3296 CheckComRCThrowRC(rc);
3297 rc = pNewDesc->init();
3298 CheckComRCThrowRC(rc);
3299
3300 /* Guest OS type */
3301 Utf8Str strOsTypeVBox,
3302 strCIMOSType = Utf8StrFmt("%RI32", (uint32_t)vsysThis.cimos);
3303 convertCIMOSType2VBoxOSType(strOsTypeVBox, vsysThis.cimos, vsysThis.strCimosDesc);
3304 pNewDesc->addEntry(VirtualSystemDescriptionType_OS,
3305 "",
3306 strCIMOSType,
3307 strOsTypeVBox);
3308
3309 /* VM name */
3310 /* If the there isn't any name specified create a default one out of
3311 * the OS type */
3312 Utf8Str nameVBox = vsysThis.strName;
3313 if (nameVBox.isEmpty())
3314 nameVBox = strOsTypeVBox;
3315 searchUniqueVMName(nameVBox);
3316 pNewDesc->addEntry(VirtualSystemDescriptionType_Name,
3317 "",
3318 vsysThis.strName,
3319 nameVBox);
3320
3321 /* VM Product */
3322 if (!vsysThis.strProduct.isEmpty())
3323 pNewDesc->addEntry(VirtualSystemDescriptionType_Product,
3324 "",
3325 vsysThis.strProduct,
3326 vsysThis.strProduct);
3327
3328 /* VM Vendor */
3329 if (!vsysThis.strVendor.isEmpty())
3330 pNewDesc->addEntry(VirtualSystemDescriptionType_Vendor,
3331 "",
3332 vsysThis.strVendor,
3333 vsysThis.strVendor);
3334
3335 /* VM Version */
3336 if (!vsysThis.strVersion.isEmpty())
3337 pNewDesc->addEntry(VirtualSystemDescriptionType_Version,
3338 "",
3339 vsysThis.strVersion,
3340 vsysThis.strVersion);
3341
3342 /* VM ProductUrl */
3343 if (!vsysThis.strProductUrl.isEmpty())
3344 pNewDesc->addEntry(VirtualSystemDescriptionType_ProductUrl,
3345 "",
3346 vsysThis.strProductUrl,
3347 vsysThis.strProductUrl);
3348
3349 /* VM VendorUrl */
3350 if (!vsysThis.strVendorUrl.isEmpty())
3351 pNewDesc->addEntry(VirtualSystemDescriptionType_VendorUrl,
3352 "",
3353 vsysThis.strVendorUrl,
3354 vsysThis.strVendorUrl);
3355
3356 /* VM description */
3357 if (!vsysThis.strDescription.isEmpty())
3358 pNewDesc->addEntry(VirtualSystemDescriptionType_Description,
3359 "",
3360 vsysThis.strDescription,
3361 vsysThis.strDescription);
3362
3363 /* VM license */
3364 if (!vsysThis.strLicenseText.isEmpty())
3365 pNewDesc->addEntry(VirtualSystemDescriptionType_License,
3366 "",
3367 vsysThis.strLicenseText,
3368 vsysThis.strLicenseText);
3369
3370 /* Now that we know the OS type, get our internal defaults based on that. */
3371 ComPtr<IGuestOSType> pGuestOSType;
3372 rc = mVirtualBox->GetGuestOSType(Bstr(strOsTypeVBox), pGuestOSType.asOutParam());
3373 CheckComRCThrowRC(rc);
3374
3375 /* CPU count */
3376 ULONG cpuCountVBox = vsysThis.cCPUs;
3377 /* Check for the constrains */
3378 if (cpuCountVBox > SchemaDefs::MaxCPUCount)
3379 {
3380 addWarning(tr("The virtual system \"%s\" claims support for %u CPU's, but VirtualBox has support for max %u CPU's only."),
3381 vsysThis.strName.c_str(), cpuCountVBox, SchemaDefs::MaxCPUCount);
3382 cpuCountVBox = SchemaDefs::MaxCPUCount;
3383 }
3384 if (vsysThis.cCPUs == 0)
3385 cpuCountVBox = 1;
3386 pNewDesc->addEntry(VirtualSystemDescriptionType_CPU,
3387 "",
3388 Utf8StrFmt("%RI32", (uint32_t)vsysThis.cCPUs),
3389 Utf8StrFmt("%RI32", (uint32_t)cpuCountVBox));
3390
3391 /* RAM */
3392 uint64_t ullMemSizeVBox = vsysThis.ullMemorySize / _1M;
3393 /* Check for the constrains */
3394 if (ullMemSizeVBox != 0 &&
3395 (ullMemSizeVBox < MM_RAM_MIN_IN_MB ||
3396 ullMemSizeVBox > MM_RAM_MAX_IN_MB))
3397 {
3398 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."),
3399 vsysThis.strName.c_str(), ullMemSizeVBox, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
3400 ullMemSizeVBox = RT_MIN(RT_MAX(ullMemSizeVBox, MM_RAM_MIN_IN_MB), MM_RAM_MAX_IN_MB);
3401 }
3402 if (vsysThis.ullMemorySize == 0)
3403 {
3404 /* If the RAM of the OVF is zero, use our predefined values */
3405 ULONG memSizeVBox2;
3406 rc = pGuestOSType->COMGETTER(RecommendedRAM)(&memSizeVBox2);
3407 CheckComRCThrowRC(rc);
3408 /* VBox stores that in MByte */
3409 ullMemSizeVBox = (uint64_t)memSizeVBox2;
3410 }
3411 pNewDesc->addEntry(VirtualSystemDescriptionType_Memory,
3412 "",
3413 Utf8StrFmt("%RI64", (uint64_t)vsysThis.ullMemorySize),
3414 Utf8StrFmt("%RI64", (uint64_t)ullMemSizeVBox));
3415
3416 /* Audio */
3417 if (!vsysThis.strSoundCardType.isEmpty())
3418 /* Currently we set the AC97 always.
3419 @todo: figure out the hardware which could be possible */
3420 pNewDesc->addEntry(VirtualSystemDescriptionType_SoundCard,
3421 "",
3422 vsysThis.strSoundCardType,
3423 Utf8StrFmt("%RI32", (uint32_t)AudioControllerType_AC97));
3424
3425#ifdef VBOX_WITH_USB
3426 /* USB Controller */
3427 if (vsysThis.fHasUsbController)
3428 pNewDesc->addEntry(VirtualSystemDescriptionType_USBController, "", "", "");
3429#endif /* VBOX_WITH_USB */
3430
3431 /* Network Controller */
3432 size_t cEthernetAdapters = vsysThis.llEthernetAdapters.size();
3433 if (cEthernetAdapters > 0)
3434 {
3435 /* Check for the constrains */
3436 if (cEthernetAdapters > SchemaDefs::NetworkAdapterCount)
3437 addWarning(tr("The virtual system \"%s\" claims support for %zu network adapters, but VirtualBox has support for max %u network adapter only."),
3438 vsysThis.strName.c_str(), cEthernetAdapters, SchemaDefs::NetworkAdapterCount);
3439
3440 /* Get the default network adapter type for the selected guest OS */
3441 NetworkAdapterType_T defaultAdapterVBox = NetworkAdapterType_Am79C970A;
3442 rc = pGuestOSType->COMGETTER(AdapterType)(&defaultAdapterVBox);
3443 CheckComRCThrowRC(rc);
3444
3445 EthernetAdaptersList::const_iterator itEA;
3446 /* Iterate through all abstract networks. We support 8 network
3447 * adapters at the maximum, so the first 8 will be added only. */
3448 size_t a = 0;
3449 for (itEA = vsysThis.llEthernetAdapters.begin();
3450 itEA != vsysThis.llEthernetAdapters.end() && a < SchemaDefs::NetworkAdapterCount;
3451 ++itEA, ++a)
3452 {
3453 const EthernetAdapter &ea = *itEA; // logical network to connect to
3454 Utf8Str strNetwork = ea.strNetworkName;
3455 // make sure it's one of these two
3456 if ( (strNetwork.compare("Null", Utf8Str::CaseInsensitive))
3457 && (strNetwork.compare("NAT", Utf8Str::CaseInsensitive))
3458 && (strNetwork.compare("Bridged", Utf8Str::CaseInsensitive))
3459 && (strNetwork.compare("Internal", Utf8Str::CaseInsensitive))
3460 && (strNetwork.compare("HostOnly", Utf8Str::CaseInsensitive))
3461 )
3462 strNetwork = "Bridged"; // VMware assumes this is the default apparently
3463
3464 /* Figure out the hardware type */
3465 NetworkAdapterType_T nwAdapterVBox = defaultAdapterVBox;
3466 if (!ea.strAdapterType.compare("PCNet32", Utf8Str::CaseInsensitive))
3467 {
3468 /* If the default adapter is already one of the two
3469 * PCNet adapters use the default one. If not use the
3470 * Am79C970A as fallback. */
3471 if (!(defaultAdapterVBox == NetworkAdapterType_Am79C970A ||
3472 defaultAdapterVBox == NetworkAdapterType_Am79C973))
3473 nwAdapterVBox = NetworkAdapterType_Am79C970A;
3474 }
3475#ifdef VBOX_WITH_E1000
3476 /* VMWare accidentally write this with VirtualCenter 3.5,
3477 so make sure in this case always to use the VMWare one */
3478 else if (!ea.strAdapterType.compare("E10000", Utf8Str::CaseInsensitive))
3479 nwAdapterVBox = NetworkAdapterType_I82545EM;
3480 else if (!ea.strAdapterType.compare("E1000", Utf8Str::CaseInsensitive))
3481 {
3482 /* Check if this OVF was written by VirtualBox */
3483 if (Utf8Str(vsysThis.strVirtualSystemType).contains("virtualbox", Utf8Str::CaseInsensitive))
3484 {
3485 /* If the default adapter is already one of the three
3486 * E1000 adapters use the default one. If not use the
3487 * I82545EM as fallback. */
3488 if (!(defaultAdapterVBox == NetworkAdapterType_I82540EM ||
3489 defaultAdapterVBox == NetworkAdapterType_I82543GC ||
3490 defaultAdapterVBox == NetworkAdapterType_I82545EM))
3491 nwAdapterVBox = NetworkAdapterType_I82540EM;
3492 }
3493 else
3494 /* Always use this one since it's what VMware uses */
3495 nwAdapterVBox = NetworkAdapterType_I82545EM;
3496 }
3497#endif /* VBOX_WITH_E1000 */
3498
3499 pNewDesc->addEntry(VirtualSystemDescriptionType_NetworkAdapter,
3500 "", // ref
3501 ea.strNetworkName, // orig
3502 Utf8StrFmt("%RI32", (uint32_t)nwAdapterVBox), // conf
3503 0,
3504 Utf8StrFmt("type=%s", strNetwork.c_str())); // extra conf
3505 }
3506 }
3507
3508 /* Floppy Drive */
3509 if (vsysThis.fHasFloppyDrive)
3510 pNewDesc->addEntry(VirtualSystemDescriptionType_Floppy, "", "", "");
3511
3512 /* CD Drive */
3513 /* @todo: I can't disable the CDROM. So nothing to do for now */
3514 /*
3515 if (vsysThis.fHasCdromDrive)
3516 pNewDesc->addEntry(VirtualSystemDescriptionType_CDROM, "", "", "");*/
3517
3518 /* Hard disk Controller */
3519 uint16_t cIDEused = 0;
3520 uint16_t cSATAused = 0; NOREF(cSATAused);
3521 uint16_t cSCSIused = 0; NOREF(cSCSIused);
3522 ControllersMap::const_iterator hdcIt;
3523 /* Iterate through all hard disk controllers */
3524 for (hdcIt = vsysThis.mapControllers.begin();
3525 hdcIt != vsysThis.mapControllers.end();
3526 ++hdcIt)
3527 {
3528 const HardDiskController &hdc = hdcIt->second;
3529 Utf8Str strControllerID = Utf8StrFmt("%RI32", (uint32_t)hdc.idController);
3530
3531 switch (hdc.system)
3532 {
3533 case HardDiskController::IDE:
3534 {
3535 /* Check for the constrains */
3536 /* @todo: I'm very confused! Are these bits *one* controller or
3537 is every port/bus declared as an extra controller. */
3538 if (cIDEused < 4)
3539 {
3540 // @todo: figure out the IDE types
3541 /* Use PIIX4 as default */
3542 Utf8Str strType = "PIIX4";
3543 if (!hdc.strControllerType.compare("PIIX3", Utf8Str::CaseInsensitive))
3544 strType = "PIIX3";
3545 else if (!hdc.strControllerType.compare("ICH6", Utf8Str::CaseInsensitive))
3546 strType = "ICH6";
3547 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerIDE,
3548 strControllerID,
3549 hdc.strControllerType,
3550 strType);
3551 }
3552 else
3553 {
3554 /* Warn only once */
3555 if (cIDEused == 1)
3556 addWarning(tr("The virtual \"%s\" system requests support for more than one IDE controller, but VirtualBox has support for only one."),
3557 vsysThis.strName.c_str());
3558
3559 }
3560 ++cIDEused;
3561 break;
3562 }
3563
3564 case HardDiskController::SATA:
3565 {
3566#ifdef VBOX_WITH_AHCI
3567 /* Check for the constrains */
3568 if (cSATAused < 1)
3569 {
3570 // @todo: figure out the SATA types
3571 /* We only support a plain AHCI controller, so use them always */
3572 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSATA,
3573 strControllerID,
3574 hdc.strControllerType,
3575 "AHCI");
3576 }
3577 else
3578 {
3579 /* Warn only once */
3580 if (cSATAused == 1)
3581 addWarning(tr("The virtual system \"%s\" requests support for more than one SATA controller, but VirtualBox has support for only one"),
3582 vsysThis.strName.c_str());
3583
3584 }
3585 ++cSATAused;
3586 break;
3587#else /* !VBOX_WITH_AHCI */
3588 addWarning(tr("The virtual system \"%s\" requests at least one SATA controller but this version of VirtualBox does not provide a SATA controller emulation"),
3589 vsysThis.strName.c_str());
3590#endif /* !VBOX_WITH_AHCI */
3591 }
3592
3593 case HardDiskController::SCSI:
3594 {
3595#ifdef VBOX_WITH_LSILOGIC
3596 /* Check for the constrains */
3597 if (cSCSIused < 1)
3598 {
3599 Utf8Str hdcController = "LsiLogic";
3600 if (!hdc.strControllerType.compare("BusLogic", Utf8Str::CaseInsensitive))
3601 hdcController = "BusLogic";
3602 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSCSI,
3603 strControllerID,
3604 hdc.strControllerType,
3605 hdcController);
3606 }
3607 else
3608 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."),
3609 vsysThis.strName.c_str(),
3610 hdc.strControllerType.c_str(),
3611 strControllerID.c_str());
3612 ++cSCSIused;
3613 break;
3614#else /* !VBOX_WITH_LSILOGIC */
3615 addWarning(tr("The virtual system \"%s\" requests at least one SATA controller but this version of VirtualBox does not provide a SCSI controller emulation"),
3616 vsysThis.strName.c_str());
3617#endif /* !VBOX_WITH_LSILOGIC */
3618 }
3619 }
3620 }
3621
3622 /* Hard disks */
3623 if (vsysThis.mapVirtualDisks.size() > 0)
3624 {
3625 VirtualDisksMap::const_iterator itVD;
3626 /* Iterate through all hard disks ()*/
3627 for (itVD = vsysThis.mapVirtualDisks.begin();
3628 itVD != vsysThis.mapVirtualDisks.end();
3629 ++itVD)
3630 {
3631 const VirtualDisk &hd = itVD->second;
3632 /* Get the associated disk image */
3633 const DiskImage &di = m->pReader->m_mapDisks[hd.strDiskId];
3634
3635 // @todo:
3636 // - figure out all possible vmdk formats we also support
3637 // - figure out if there is a url specifier for vhd already
3638 // - we need a url specifier for the vdi format
3639 if ( di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#sparse", Utf8Str::CaseInsensitive)
3640 || di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive))
3641 {
3642 /* If the href is empty use the VM name as filename */
3643 Utf8Str strFilename = di.strHref;
3644 if (!strFilename.length())
3645 strFilename = Utf8StrFmt("%s.vmdk", nameVBox.c_str());
3646 /* Construct a unique target path */
3647 Utf8StrFmt strPath("%ls%c%s",
3648 bstrDefaultHardDiskLocation.raw(),
3649 RTPATH_DELIMITER,
3650 strFilename.c_str());
3651 searchUniqueDiskImageFilePath(strPath);
3652
3653 /* find the description for the hard disk controller
3654 * that has the same ID as hd.idController */
3655 const VirtualSystemDescriptionEntry *pController;
3656 if (!(pController = pNewDesc->findControllerFromID(hd.idController)))
3657 throw setError(E_FAIL,
3658 tr("Cannot find hard disk controller with OVF instance ID %RI32 to which disk \"%s\" should be attached"),
3659 hd.idController,
3660 di.strHref.c_str());
3661
3662 /* controller to attach to, and the bus within that controller */
3663 Utf8StrFmt strExtraConfig("controller=%RI16;channel=%RI16",
3664 pController->ulIndex,
3665 hd.ulAddressOnParent);
3666 ULONG ulSize = 0;
3667 if (di.iCapacity != -1)
3668 ulSize = (ULONG)(di.iCapacity / _1M);
3669 else if (di.iPopulatedSize != -1)
3670 ulSize = (ULONG)(di.iPopulatedSize / _1M);
3671 else if (di.iSize != -1)
3672 ulSize = (ULONG)(di.iSize / _1M);
3673 if (ulSize == 0)
3674 ulSize = 10000; // assume 10 GB, this is for the progress bar only anyway
3675 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskImage,
3676 hd.strDiskId,
3677 di.strHref,
3678 strPath,
3679 ulSize,
3680 strExtraConfig);
3681 }
3682 else
3683 throw setError(VBOX_E_FILE_ERROR,
3684 tr("Unsupported format for virtual disk image in OVF: \"%s\"", di.strFormat.c_str()));
3685 }
3686 }
3687
3688 m->virtualSystemDescriptions.push_back(pNewDesc);
3689 }
3690 }
3691 catch (HRESULT aRC)
3692 {
3693 /* On error we clear the list & return */
3694 m->virtualSystemDescriptions.clear();
3695 rc = aRC;
3696 }
3697
3698 return rc;
3699}
3700
3701/**
3702 * Public method implementation.
3703 * @param aProgress
3704 * @return
3705 */
3706STDMETHODIMP Appliance::ImportMachines(IProgress **aProgress)
3707{
3708 CheckComArgOutPointerValid(aProgress);
3709
3710 AutoCaller autoCaller(this);
3711 CheckComRCReturnRC(autoCaller.rc());
3712
3713 AutoReadLock(this);
3714
3715 if (!m->pReader)
3716 return setError(E_FAIL,
3717 tr("Cannot import machines without reading it first (call read() before importMachines())"));
3718
3719 ComObjPtr<Progress> progress;
3720 HRESULT rc = S_OK;
3721 try
3722 {
3723 rc = importImpl(m->locInfo, progress);
3724 }
3725 catch (HRESULT aRC)
3726 {
3727 rc = aRC;
3728 }
3729
3730 if (SUCCEEDED(rc))
3731 /* Return progress to the caller */
3732 progress.queryInterfaceTo(aProgress);
3733
3734 return rc;
3735}
3736
3737STDMETHODIMP Appliance::CreateVFSExplorer(IN_BSTR aURI, IVFSExplorer **aExplorer)
3738{
3739 CheckComArgOutPointerValid(aExplorer);
3740
3741 AutoCaller autoCaller(this);
3742 CheckComRCReturnRC(autoCaller.rc());
3743
3744 AutoReadLock(this);
3745
3746 ComObjPtr<VFSExplorer> explorer;
3747 HRESULT rc = S_OK;
3748 try
3749 {
3750 Utf8Str uri(aURI);
3751 /* Check which kind of export the user has requested */
3752 LocationInfo li;
3753 parseURI(uri, li);
3754 /* Create the explorer object */
3755 explorer.createObject();
3756 rc = explorer->init(li.storageType, li.strPath, li.strHostname, li.strUsername, li.strPassword, mVirtualBox);
3757 }
3758 catch (HRESULT aRC)
3759 {
3760 rc = aRC;
3761 }
3762
3763 if (SUCCEEDED(rc))
3764 /* Return explorer to the caller */
3765 explorer.queryInterfaceTo(aExplorer);
3766
3767 return rc;
3768}
3769
3770STDMETHODIMP Appliance::Write(IN_BSTR format, IN_BSTR path, IProgress **aProgress)
3771{
3772 if (!path) return E_POINTER;
3773 CheckComArgOutPointerValid(aProgress);
3774
3775 AutoCaller autoCaller(this);
3776 CheckComRCReturnRC(autoCaller.rc());
3777
3778 AutoWriteLock(this);
3779
3780 // see if we can handle this file; for now we insist it has an ".ovf" extension
3781 Utf8Str strPath = path;
3782 if (!strPath.endsWith(".ovf", Utf8Str::CaseInsensitive))
3783 return setError(VBOX_E_FILE_ERROR,
3784 tr("Appliance file must have .ovf extension"));
3785
3786 Utf8Str strFormat(format);
3787 TaskExportOVF::OVFFormat ovfF;
3788 if (strFormat == "ovf-0.9")
3789 ovfF = TaskExportOVF::OVF_0_9;
3790 else if (strFormat == "ovf-1.0")
3791 ovfF = TaskExportOVF::OVF_1_0;
3792 else
3793 return setError(VBOX_E_FILE_ERROR,
3794 tr("Invalid format \"%s\" specified"), strFormat.c_str());
3795
3796 ComObjPtr<Progress> progress;
3797 HRESULT rc = S_OK;
3798 try
3799 {
3800 /* Parse all necessary info out of the URI */
3801 parseURI(strPath, m->locInfo);
3802 rc = writeImpl(ovfF, m->locInfo, progress);
3803 }
3804 catch (HRESULT aRC)
3805 {
3806 rc = aRC;
3807 }
3808
3809 if (SUCCEEDED(rc))
3810 /* Return progress to the caller */
3811 progress.queryInterfaceTo(aProgress);
3812
3813 return rc;
3814}
3815
3816/**
3817* Public method implementation.
3818 * @return
3819 */
3820STDMETHODIMP Appliance::GetWarnings(ComSafeArrayOut(BSTR, aWarnings))
3821{
3822 if (ComSafeArrayOutIsNull(aWarnings))
3823 return E_POINTER;
3824
3825 AutoCaller autoCaller(this);
3826 CheckComRCReturnRC(autoCaller.rc());
3827
3828 AutoReadLock alock(this);
3829
3830 com::SafeArray<BSTR> sfaWarnings(m->llWarnings.size());
3831
3832 list<Utf8Str>::const_iterator it;
3833 size_t i = 0;
3834 for (it = m->llWarnings.begin();
3835 it != m->llWarnings.end();
3836 ++it, ++i)
3837 {
3838 Bstr bstr = *it;
3839 bstr.cloneTo(&sfaWarnings[i]);
3840 }
3841
3842 sfaWarnings.detachTo(ComSafeArrayOutArg(aWarnings));
3843
3844 return S_OK;
3845}
3846
3847////////////////////////////////////////////////////////////////////////////////
3848//
3849// IVirtualSystemDescription constructor / destructor
3850//
3851////////////////////////////////////////////////////////////////////////////////
3852
3853DEFINE_EMPTY_CTOR_DTOR(VirtualSystemDescription)
3854
3855/**
3856 * COM initializer.
3857 * @return
3858 */
3859HRESULT VirtualSystemDescription::init()
3860{
3861 /* Enclose the state transition NotReady->InInit->Ready */
3862 AutoInitSpan autoInitSpan(this);
3863 AssertReturn(autoInitSpan.isOk(), E_FAIL);
3864
3865 /* Initialize data */
3866 m = new Data();
3867
3868 /* Confirm a successful initialization */
3869 autoInitSpan.setSucceeded();
3870 return S_OK;
3871}
3872
3873/**
3874* COM uninitializer.
3875*/
3876
3877void VirtualSystemDescription::uninit()
3878{
3879 delete m;
3880 m = NULL;
3881}
3882
3883////////////////////////////////////////////////////////////////////////////////
3884//
3885// IVirtualSystemDescription public methods
3886//
3887////////////////////////////////////////////////////////////////////////////////
3888
3889/**
3890 * Public method implementation.
3891 * @param
3892 * @return
3893 */
3894STDMETHODIMP VirtualSystemDescription::COMGETTER(Count)(ULONG *aCount)
3895{
3896 if (!aCount)
3897 return E_POINTER;
3898
3899 AutoCaller autoCaller(this);
3900 CheckComRCReturnRC(autoCaller.rc());
3901
3902 AutoReadLock alock(this);
3903
3904 *aCount = (ULONG)m->llDescriptions.size();
3905
3906 return S_OK;
3907}
3908
3909/**
3910 * Public method implementation.
3911 * @return
3912 */
3913STDMETHODIMP VirtualSystemDescription::GetDescription(ComSafeArrayOut(VirtualSystemDescriptionType_T, aTypes),
3914 ComSafeArrayOut(BSTR, aRefs),
3915 ComSafeArrayOut(BSTR, aOrigValues),
3916 ComSafeArrayOut(BSTR, aVboxValues),
3917 ComSafeArrayOut(BSTR, aExtraConfigValues))
3918{
3919 if (ComSafeArrayOutIsNull(aTypes) ||
3920 ComSafeArrayOutIsNull(aRefs) ||
3921 ComSafeArrayOutIsNull(aOrigValues) ||
3922 ComSafeArrayOutIsNull(aVboxValues) ||
3923 ComSafeArrayOutIsNull(aExtraConfigValues))
3924 return E_POINTER;
3925
3926 AutoCaller autoCaller(this);
3927 CheckComRCReturnRC(autoCaller.rc());
3928
3929 AutoReadLock alock(this);
3930
3931 ULONG c = (ULONG)m->llDescriptions.size();
3932 com::SafeArray<VirtualSystemDescriptionType_T> sfaTypes(c);
3933 com::SafeArray<BSTR> sfaRefs(c);
3934 com::SafeArray<BSTR> sfaOrigValues(c);
3935 com::SafeArray<BSTR> sfaVboxValues(c);
3936 com::SafeArray<BSTR> sfaExtraConfigValues(c);
3937
3938 list<VirtualSystemDescriptionEntry>::const_iterator it;
3939 size_t i = 0;
3940 for (it = m->llDescriptions.begin();
3941 it != m->llDescriptions.end();
3942 ++it, ++i)
3943 {
3944 const VirtualSystemDescriptionEntry &vsde = (*it);
3945
3946 sfaTypes[i] = vsde.type;
3947
3948 Bstr bstr = vsde.strRef;
3949 bstr.cloneTo(&sfaRefs[i]);
3950
3951 bstr = vsde.strOvf;
3952 bstr.cloneTo(&sfaOrigValues[i]);
3953
3954 bstr = vsde.strVbox;
3955 bstr.cloneTo(&sfaVboxValues[i]);
3956
3957 bstr = vsde.strExtraConfig;
3958 bstr.cloneTo(&sfaExtraConfigValues[i]);
3959 }
3960
3961 sfaTypes.detachTo(ComSafeArrayOutArg(aTypes));
3962 sfaRefs.detachTo(ComSafeArrayOutArg(aRefs));
3963 sfaOrigValues.detachTo(ComSafeArrayOutArg(aOrigValues));
3964 sfaVboxValues.detachTo(ComSafeArrayOutArg(aVboxValues));
3965 sfaExtraConfigValues.detachTo(ComSafeArrayOutArg(aExtraConfigValues));
3966
3967 return S_OK;
3968}
3969
3970/**
3971 * Public method implementation.
3972 * @return
3973 */
3974STDMETHODIMP VirtualSystemDescription::GetDescriptionByType(VirtualSystemDescriptionType_T aType,
3975 ComSafeArrayOut(VirtualSystemDescriptionType_T, aTypes),
3976 ComSafeArrayOut(BSTR, aRefs),
3977 ComSafeArrayOut(BSTR, aOrigValues),
3978 ComSafeArrayOut(BSTR, aVboxValues),
3979 ComSafeArrayOut(BSTR, aExtraConfigValues))
3980{
3981 if (ComSafeArrayOutIsNull(aTypes) ||
3982 ComSafeArrayOutIsNull(aRefs) ||
3983 ComSafeArrayOutIsNull(aOrigValues) ||
3984 ComSafeArrayOutIsNull(aVboxValues) ||
3985 ComSafeArrayOutIsNull(aExtraConfigValues))
3986 return E_POINTER;
3987
3988 AutoCaller autoCaller(this);
3989 CheckComRCReturnRC(autoCaller.rc());
3990
3991 AutoReadLock alock(this);
3992
3993 std::list<VirtualSystemDescriptionEntry*> vsd = findByType (aType);
3994 ULONG c = (ULONG)vsd.size();
3995 com::SafeArray<VirtualSystemDescriptionType_T> sfaTypes(c);
3996 com::SafeArray<BSTR> sfaRefs(c);
3997 com::SafeArray<BSTR> sfaOrigValues(c);
3998 com::SafeArray<BSTR> sfaVboxValues(c);
3999 com::SafeArray<BSTR> sfaExtraConfigValues(c);
4000
4001 list<VirtualSystemDescriptionEntry*>::const_iterator it;
4002 size_t i = 0;
4003 for (it = vsd.begin();
4004 it != vsd.end();
4005 ++it, ++i)
4006 {
4007 const VirtualSystemDescriptionEntry *vsde = (*it);
4008
4009 sfaTypes[i] = vsde->type;
4010
4011 Bstr bstr = vsde->strRef;
4012 bstr.cloneTo(&sfaRefs[i]);
4013
4014 bstr = vsde->strOvf;
4015 bstr.cloneTo(&sfaOrigValues[i]);
4016
4017 bstr = vsde->strVbox;
4018 bstr.cloneTo(&sfaVboxValues[i]);
4019
4020 bstr = vsde->strExtraConfig;
4021 bstr.cloneTo(&sfaExtraConfigValues[i]);
4022 }
4023
4024 sfaTypes.detachTo(ComSafeArrayOutArg(aTypes));
4025 sfaRefs.detachTo(ComSafeArrayOutArg(aRefs));
4026 sfaOrigValues.detachTo(ComSafeArrayOutArg(aOrigValues));
4027 sfaVboxValues.detachTo(ComSafeArrayOutArg(aVboxValues));
4028 sfaExtraConfigValues.detachTo(ComSafeArrayOutArg(aExtraConfigValues));
4029
4030 return S_OK;
4031}
4032
4033/**
4034 * Public method implementation.
4035 * @return
4036 */
4037STDMETHODIMP VirtualSystemDescription::GetValuesByType(VirtualSystemDescriptionType_T aType,
4038 VirtualSystemDescriptionValueType_T aWhich,
4039 ComSafeArrayOut(BSTR, aValues))
4040{
4041 if (ComSafeArrayOutIsNull(aValues))
4042 return E_POINTER;
4043
4044 AutoCaller autoCaller(this);
4045 CheckComRCReturnRC(autoCaller.rc());
4046
4047 AutoReadLock alock(this);
4048
4049 std::list<VirtualSystemDescriptionEntry*> vsd = findByType (aType);
4050 com::SafeArray<BSTR> sfaValues((ULONG)vsd.size());
4051
4052 list<VirtualSystemDescriptionEntry*>::const_iterator it;
4053 size_t i = 0;
4054 for (it = vsd.begin();
4055 it != vsd.end();
4056 ++it, ++i)
4057 {
4058 const VirtualSystemDescriptionEntry *vsde = (*it);
4059
4060 Bstr bstr;
4061 switch (aWhich)
4062 {
4063 case VirtualSystemDescriptionValueType_Reference: bstr = vsde->strRef; break;
4064 case VirtualSystemDescriptionValueType_Original: bstr = vsde->strOvf; break;
4065 case VirtualSystemDescriptionValueType_Auto: bstr = vsde->strVbox; break;
4066 case VirtualSystemDescriptionValueType_ExtraConfig: bstr = vsde->strExtraConfig; break;
4067 }
4068
4069 bstr.cloneTo(&sfaValues[i]);
4070 }
4071
4072 sfaValues.detachTo(ComSafeArrayOutArg(aValues));
4073
4074 return S_OK;
4075}
4076
4077/**
4078 * Public method implementation.
4079 * @return
4080 */
4081STDMETHODIMP VirtualSystemDescription::SetFinalValues(ComSafeArrayIn(BOOL, aEnabled),
4082 ComSafeArrayIn(IN_BSTR, argVboxValues),
4083 ComSafeArrayIn(IN_BSTR, argExtraConfigValues))
4084{
4085#ifndef RT_OS_WINDOWS
4086 NOREF(aEnabledSize);
4087#endif /* RT_OS_WINDOWS */
4088
4089 CheckComArgSafeArrayNotNull(aEnabled);
4090 CheckComArgSafeArrayNotNull(argVboxValues);
4091 CheckComArgSafeArrayNotNull(argExtraConfigValues);
4092
4093 AutoCaller autoCaller(this);
4094 CheckComRCReturnRC(autoCaller.rc());
4095
4096 AutoWriteLock alock(this);
4097
4098 com::SafeArray<BOOL> sfaEnabled(ComSafeArrayInArg(aEnabled));
4099 com::SafeArray<IN_BSTR> sfaVboxValues(ComSafeArrayInArg(argVboxValues));
4100 com::SafeArray<IN_BSTR> sfaExtraConfigValues(ComSafeArrayInArg(argExtraConfigValues));
4101
4102 if ( (sfaEnabled.size() != m->llDescriptions.size())
4103 || (sfaVboxValues.size() != m->llDescriptions.size())
4104 || (sfaExtraConfigValues.size() != m->llDescriptions.size())
4105 )
4106 return E_INVALIDARG;
4107
4108 list<VirtualSystemDescriptionEntry>::iterator it;
4109 size_t i = 0;
4110 for (it = m->llDescriptions.begin();
4111 it != m->llDescriptions.end();
4112 ++it, ++i)
4113 {
4114 VirtualSystemDescriptionEntry& vsde = *it;
4115
4116 if (sfaEnabled[i])
4117 {
4118 vsde.strVbox = sfaVboxValues[i];
4119 vsde.strExtraConfig = sfaExtraConfigValues[i];
4120 }
4121 else
4122 vsde.type = VirtualSystemDescriptionType_Ignore;
4123 }
4124
4125 return S_OK;
4126}
4127
4128/**
4129 * Public method implementation.
4130 * @return
4131 */
4132STDMETHODIMP VirtualSystemDescription::AddDescription(VirtualSystemDescriptionType_T aType,
4133 IN_BSTR aVboxValue,
4134 IN_BSTR aExtraConfigValue)
4135{
4136 CheckComArgNotNull(aVboxValue);
4137 CheckComArgNotNull(aExtraConfigValue);
4138
4139 AutoCaller autoCaller(this);
4140 CheckComRCReturnRC(autoCaller.rc());
4141
4142 AutoWriteLock alock(this);
4143
4144 addEntry(aType, "", aVboxValue, aVboxValue, 0, aExtraConfigValue);
4145
4146 return S_OK;
4147}
4148
4149/**
4150 * Internal method; adds a new description item to the member list.
4151 * @param aType Type of description for the new item.
4152 * @param strRef Reference item; only used with hard disk controllers.
4153 * @param aOrigValue Corresponding original value from OVF.
4154 * @param aAutoValue Initial configuration value (can be overridden by caller with setFinalValues).
4155 * @param ulSizeMB Weight for IProgress
4156 * @param strExtraConfig Extra configuration; meaning dependent on type.
4157 */
4158void VirtualSystemDescription::addEntry(VirtualSystemDescriptionType_T aType,
4159 const Utf8Str &strRef,
4160 const Utf8Str &aOrigValue,
4161 const Utf8Str &aAutoValue,
4162 uint32_t ulSizeMB,
4163 const Utf8Str &strExtraConfig /*= ""*/)
4164{
4165 VirtualSystemDescriptionEntry vsde;
4166 vsde.ulIndex = (uint32_t)m->llDescriptions.size(); // each entry gets an index so the client side can reference them
4167 vsde.type = aType;
4168 vsde.strRef = strRef;
4169 vsde.strOvf = aOrigValue;
4170 vsde.strVbox = aAutoValue;
4171 vsde.strExtraConfig = strExtraConfig;
4172 vsde.ulSizeMB = ulSizeMB;
4173
4174 m->llDescriptions.push_back(vsde);
4175}
4176
4177/**
4178 * Private method; returns a list of description items containing all the items from the member
4179 * description items of this virtual system that match the given type.
4180 * @param aType
4181 * @return
4182 */
4183std::list<VirtualSystemDescriptionEntry*> VirtualSystemDescription::findByType(VirtualSystemDescriptionType_T aType)
4184{
4185 std::list<VirtualSystemDescriptionEntry*> vsd;
4186
4187 list<VirtualSystemDescriptionEntry>::iterator it;
4188 for (it = m->llDescriptions.begin();
4189 it != m->llDescriptions.end();
4190 ++it)
4191 {
4192 if (it->type == aType)
4193 vsd.push_back(&(*it));
4194 }
4195
4196 return vsd;
4197}
4198
4199/**
4200 * Private method; looks thru the member hardware items for the IDE, SATA, or SCSI controller with
4201 * the given reference ID. Useful when needing the controller for a particular
4202 * virtual disk.
4203 * @param id
4204 * @return
4205 */
4206const VirtualSystemDescriptionEntry* VirtualSystemDescription::findControllerFromID(uint32_t id)
4207{
4208 Utf8Str strRef = Utf8StrFmt("%RI32", id);
4209 list<VirtualSystemDescriptionEntry>::const_iterator it;
4210 for (it = m->llDescriptions.begin();
4211 it != m->llDescriptions.end();
4212 ++it)
4213 {
4214 const VirtualSystemDescriptionEntry &d = *it;
4215 switch (d.type)
4216 {
4217 case VirtualSystemDescriptionType_HardDiskControllerIDE:
4218 case VirtualSystemDescriptionType_HardDiskControllerSATA:
4219 case VirtualSystemDescriptionType_HardDiskControllerSCSI:
4220 if (d.strRef == strRef)
4221 return &d;
4222 break;
4223 }
4224 }
4225
4226 return NULL;
4227}
4228
4229////////////////////////////////////////////////////////////////////////////////
4230//
4231// IMachine public methods
4232//
4233////////////////////////////////////////////////////////////////////////////////
4234
4235// This code is here so we won't have to include the appliance headers in the
4236// IMachine implementation, and we also need to access private appliance data.
4237
4238/**
4239* Public method implementation.
4240* @param appliance
4241* @return
4242*/
4243
4244STDMETHODIMP Machine::Export(IAppliance *aAppliance, IVirtualSystemDescription **aDescription)
4245{
4246 HRESULT rc = S_OK;
4247
4248 if (!aAppliance)
4249 return E_POINTER;
4250
4251 AutoCaller autoCaller(this);
4252 CheckComRCReturnRC(autoCaller.rc());
4253
4254 AutoReadLock alock(this);
4255
4256 ComObjPtr<VirtualSystemDescription> pNewDesc;
4257
4258 try
4259 {
4260 Bstr bstrName;
4261 Bstr bstrDescription;
4262 Bstr bstrGuestOSType;
4263 uint32_t cCPUs;
4264 uint32_t ulMemSizeMB;
4265 BOOL fDVDEnabled;
4266 BOOL fFloppyEnabled;
4267 BOOL fUSBEnabled;
4268 BOOL fAudioEnabled;
4269 AudioControllerType_T audioController;
4270
4271 ComPtr<IUSBController> pUsbController;
4272 ComPtr<IAudioAdapter> pAudioAdapter;
4273
4274 // get name
4275 bstrName = mUserData->mName;
4276 // get description
4277 bstrDescription = mUserData->mDescription;
4278 // get guest OS
4279 bstrGuestOSType = mUserData->mOSTypeId;
4280 // CPU count
4281 cCPUs = mHWData->mCPUCount;
4282 // memory size in MB
4283 ulMemSizeMB = mHWData->mMemorySize;
4284 // VRAM size?
4285 // BIOS settings?
4286 // 3D acceleration enabled?
4287 // hardware virtualization enabled?
4288 // nested paging enabled?
4289 // HWVirtExVPIDEnabled?
4290 // PAEEnabled?
4291 // snapshotFolder?
4292 // VRDPServer?
4293
4294 // floppy
4295 rc = mFloppyDrive->COMGETTER(Enabled)(&fFloppyEnabled);
4296 if (FAILED(rc)) throw rc;
4297
4298 // CD-ROM ?!?
4299 // ComPtr<IDVDDrive> pDVDDrive;
4300 fDVDEnabled = 1;
4301
4302 // this is more tricky so use the COM method
4303 rc = COMGETTER(USBController)(pUsbController.asOutParam());
4304 if (FAILED(rc))
4305 fUSBEnabled = false;
4306 else
4307 rc = pUsbController->COMGETTER(Enabled)(&fUSBEnabled);
4308
4309 pAudioAdapter = mAudioAdapter;
4310 rc = pAudioAdapter->COMGETTER(Enabled)(&fAudioEnabled);
4311 if (FAILED(rc)) throw rc;
4312 rc = pAudioAdapter->COMGETTER(AudioController)(&audioController);
4313 if (FAILED(rc)) throw rc;
4314
4315 // create a new virtual system
4316 rc = pNewDesc.createObject();
4317 CheckComRCThrowRC(rc);
4318 rc = pNewDesc->init();
4319 CheckComRCThrowRC(rc);
4320
4321 /* Guest OS type */
4322 Utf8Str strOsTypeVBox(bstrGuestOSType);
4323 CIMOSType_T cim = convertVBoxOSType2CIMOSType(strOsTypeVBox.c_str());
4324 pNewDesc->addEntry(VirtualSystemDescriptionType_OS,
4325 "",
4326 Utf8StrFmt("%RI32", cim),
4327 strOsTypeVBox);
4328
4329 /* VM name */
4330 Utf8Str strVMName(bstrName);
4331 pNewDesc->addEntry(VirtualSystemDescriptionType_Name,
4332 "",
4333 strVMName,
4334 strVMName);
4335
4336 // description
4337 Utf8Str strDescription(bstrDescription);
4338 pNewDesc->addEntry(VirtualSystemDescriptionType_Description,
4339 "",
4340 strDescription,
4341 strDescription);
4342
4343 /* CPU count*/
4344 Utf8Str strCpuCount = Utf8StrFmt("%RI32", cCPUs);
4345 pNewDesc->addEntry(VirtualSystemDescriptionType_CPU,
4346 "",
4347 strCpuCount,
4348 strCpuCount);
4349
4350 /* Memory */
4351 Utf8Str strMemory = Utf8StrFmt("%RI32", (uint64_t)ulMemSizeMB * _1M);
4352 pNewDesc->addEntry(VirtualSystemDescriptionType_Memory,
4353 "",
4354 strMemory,
4355 strMemory);
4356
4357 int32_t lIDEControllerIndex = 0;
4358 int32_t lSATAControllerIndex = 0;
4359 int32_t lSCSIControllerIndex = 0;
4360
4361// <const name="HardDiskControllerIDE" value="6" />
4362 ComPtr<IStorageController> pController;
4363 rc = GetStorageControllerByName(Bstr("IDE"), pController.asOutParam());
4364 if (FAILED(rc)) throw rc;
4365 Utf8Str strVbox;
4366 StorageControllerType_T ctlr;
4367 rc = pController->COMGETTER(ControllerType)(&ctlr);
4368 if (FAILED(rc)) throw rc;
4369 switch(ctlr)
4370 {
4371 case StorageControllerType_PIIX3: strVbox = "PIIX3"; break;
4372 case StorageControllerType_PIIX4: strVbox = "PIIX4"; break;
4373 case StorageControllerType_ICH6: strVbox = "ICH6"; break;
4374 }
4375
4376 if (strVbox.length())
4377 {
4378 lIDEControllerIndex = (int32_t)pNewDesc->m->llDescriptions.size();
4379 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerIDE,
4380 Utf8StrFmt("%d", lIDEControllerIndex),
4381 strVbox,
4382 strVbox);
4383 }
4384
4385#ifdef VBOX_WITH_AHCI
4386// <const name="HardDiskControllerSATA" value="7" />
4387 rc = GetStorageControllerByName(Bstr("SATA"), pController.asOutParam());
4388 if (SUCCEEDED(rc))
4389 {
4390 strVbox = "AHCI";
4391 lSATAControllerIndex = (int32_t)pNewDesc->m->llDescriptions.size();
4392 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSATA,
4393 Utf8StrFmt("%d", lSATAControllerIndex),
4394 strVbox,
4395 strVbox);
4396 }
4397#endif // VBOX_WITH_AHCI
4398
4399#ifdef VBOX_WITH_LSILOGIC
4400// <const name="HardDiskControllerSCSI" value="8" />
4401 rc = GetStorageControllerByName(Bstr("SCSI"), pController.asOutParam());
4402 if (SUCCEEDED(rc))
4403 {
4404 rc = pController->COMGETTER(ControllerType)(&ctlr);
4405 if (SUCCEEDED(rc))
4406 {
4407 strVbox = "LsiLogic"; // the default in VBox
4408 switch(ctlr)
4409 {
4410 case StorageControllerType_LsiLogic: strVbox = "LsiLogic"; break;
4411 case StorageControllerType_BusLogic: strVbox = "BusLogic"; break;
4412 }
4413 lSCSIControllerIndex = (int32_t)pNewDesc->m->llDescriptions.size();
4414 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSCSI,
4415 Utf8StrFmt("%d", lSCSIControllerIndex),
4416 strVbox,
4417 strVbox);
4418 }
4419 else
4420 throw rc;
4421 }
4422#endif // VBOX_WITH_LSILOGIC
4423
4424// <const name="HardDiskImage" value="9" />
4425 HDData::AttachmentList::iterator itA;
4426 for (itA = mHDData->mAttachments.begin();
4427 itA != mHDData->mAttachments.end();
4428 ++itA)
4429 {
4430 ComObjPtr<HardDiskAttachment> pHDA = *itA;
4431
4432 // the attachment's data
4433 ComPtr<IHardDisk> pHardDisk;
4434 ComPtr<IStorageController> ctl;
4435 Bstr controllerName;
4436
4437 rc = pHDA->COMGETTER(Controller)(controllerName.asOutParam());
4438 if (FAILED(rc)) throw rc;
4439
4440 rc = GetStorageControllerByName(controllerName, ctl.asOutParam());
4441 if (FAILED(rc)) throw rc;
4442
4443 StorageBus_T storageBus;
4444 LONG lChannel;
4445 LONG lDevice;
4446
4447 rc = ctl->COMGETTER(Bus)(&storageBus);
4448 if (FAILED(rc)) throw rc;
4449
4450 rc = pHDA->COMGETTER(HardDisk)(pHardDisk.asOutParam());
4451 if (FAILED(rc)) throw rc;
4452
4453 rc = pHDA->COMGETTER(Port)(&lChannel);
4454 if (FAILED(rc)) throw rc;
4455
4456 rc = pHDA->COMGETTER(Device)(&lDevice);
4457 if (FAILED(rc)) throw rc;
4458
4459 Bstr bstrLocation;
4460 rc = pHardDisk->COMGETTER(Location)(bstrLocation.asOutParam());
4461 if (FAILED(rc)) throw rc;
4462 Bstr bstrName;
4463 rc = pHardDisk->COMGETTER(Name)(bstrName.asOutParam());
4464 if (FAILED(rc)) throw rc;
4465
4466 // force reading state, or else size will be returned as 0
4467 MediaState_T ms;
4468 rc = pHardDisk->COMGETTER(State)(&ms);
4469 if (FAILED(rc)) throw rc;
4470
4471 ULONG64 ullSize;
4472 rc = pHardDisk->COMGETTER(Size)(&ullSize);
4473 if (FAILED(rc)) throw rc;
4474
4475 // and how this translates to the virtual system
4476 int32_t lControllerVsys = 0;
4477 LONG lChannelVsys;
4478
4479 switch (storageBus)
4480 {
4481 case StorageBus_IDE:
4482 // this is the exact reverse to what we're doing in Appliance::taskThreadImportMachines,
4483 // and it must be updated when that is changed!
4484
4485 if (lChannel == 0 && lDevice == 0) // primary master
4486 lChannelVsys = 0;
4487 else if (lChannel == 0 && lDevice == 1) // primary slave
4488 lChannelVsys = 1;
4489 else if (lChannel == 1 && lDevice == 1) // secondary slave; secondary master is always CDROM
4490 lChannelVsys = 2;
4491 else
4492 throw setError(VBOX_E_NOT_SUPPORTED,
4493 tr("Cannot handle hard disk attachment: channel is %d, device is %d"), lChannel, lDevice);
4494
4495 lControllerVsys = lIDEControllerIndex;
4496 break;
4497
4498 case StorageBus_SATA:
4499 lChannelVsys = lChannel; // should be between 0 and 29
4500 lControllerVsys = lSATAControllerIndex;
4501 break;
4502
4503 case StorageBus_SCSI:
4504 lChannelVsys = lChannel; // should be between 0 and 15
4505 lControllerVsys = lSCSIControllerIndex;
4506 break;
4507
4508 default:
4509 throw setError(VBOX_E_NOT_SUPPORTED,
4510 tr("Cannot handle hard disk attachment: storageBus is %d, channel is %d, device is %d"), storageBus, lChannel, lDevice);
4511 break;
4512 }
4513
4514 Utf8Str strTargetVmdkName(bstrName);
4515 strTargetVmdkName.stripExt();
4516 strTargetVmdkName.append(".vmdk");
4517
4518 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskImage,
4519 strTargetVmdkName, // disk ID: let's use the name
4520 strTargetVmdkName, // OVF value:
4521 Utf8Str(bstrLocation), // vbox value: media path
4522 (uint32_t)(ullSize / _1M),
4523 Utf8StrFmt("controller=%RI32;channel=%RI32", lControllerVsys, lChannelVsys));
4524 }
4525
4526 /* Floppy Drive */
4527 if (fFloppyEnabled)
4528 pNewDesc->addEntry(VirtualSystemDescriptionType_Floppy, "", "", "");
4529
4530 /* CD Drive */
4531 if (fDVDEnabled)
4532 pNewDesc->addEntry(VirtualSystemDescriptionType_CDROM, "", "", "");
4533
4534// <const name="NetworkAdapter" />
4535 size_t a;
4536 for (a = 0;
4537 a < SchemaDefs::NetworkAdapterCount;
4538 ++a)
4539 {
4540 ComPtr<INetworkAdapter> pNetworkAdapter;
4541 BOOL fEnabled;
4542 NetworkAdapterType_T adapterType;
4543 NetworkAttachmentType_T attachmentType;
4544
4545 rc = GetNetworkAdapter((ULONG)a, pNetworkAdapter.asOutParam());
4546 if (FAILED(rc)) throw rc;
4547 /* Enable the network card & set the adapter type */
4548 rc = pNetworkAdapter->COMGETTER(Enabled)(&fEnabled);
4549 if (FAILED(rc)) throw rc;
4550
4551 if (fEnabled)
4552 {
4553 Utf8Str strAttachmentType;
4554
4555 rc = pNetworkAdapter->COMGETTER(AdapterType)(&adapterType);
4556 if (FAILED(rc)) throw rc;
4557
4558 rc = pNetworkAdapter->COMGETTER(AttachmentType)(&attachmentType);
4559 if (FAILED(rc)) throw rc;
4560
4561 switch (attachmentType)
4562 {
4563 case NetworkAttachmentType_Null:
4564 strAttachmentType = "Null";
4565 break;
4566
4567 case NetworkAttachmentType_NAT:
4568 strAttachmentType = "NAT";
4569 break;
4570
4571 case NetworkAttachmentType_Bridged:
4572 strAttachmentType = "Bridged";
4573 break;
4574
4575 case NetworkAttachmentType_Internal:
4576 strAttachmentType = "Internal";
4577 break;
4578
4579 case NetworkAttachmentType_HostOnly:
4580 strAttachmentType = "HostOnly";
4581 break;
4582 }
4583
4584 pNewDesc->addEntry(VirtualSystemDescriptionType_NetworkAdapter,
4585 "", // ref
4586 strAttachmentType, // orig
4587 Utf8StrFmt("%RI32", (uint32_t)adapterType), // conf
4588 0,
4589 Utf8StrFmt("type=%s", strAttachmentType.c_str())); // extra conf
4590 }
4591 }
4592
4593// <const name="USBController" />
4594#ifdef VBOX_WITH_USB
4595 if (fUSBEnabled)
4596 pNewDesc->addEntry(VirtualSystemDescriptionType_USBController, "", "", "");
4597#endif /* VBOX_WITH_USB */
4598
4599// <const name="SoundCard" />
4600 if (fAudioEnabled)
4601 {
4602 pNewDesc->addEntry(VirtualSystemDescriptionType_SoundCard,
4603 "",
4604 "ensoniq1371", // this is what OVFTool writes and VMware supports
4605 Utf8StrFmt("%RI32", audioController));
4606 }
4607
4608 // finally, add the virtual system to the appliance
4609 Appliance *pAppliance = static_cast<Appliance*>(aAppliance);
4610 AutoCaller autoCaller1(pAppliance);
4611 CheckComRCReturnRC(autoCaller1.rc());
4612
4613 /* We return the new description to the caller */
4614 ComPtr<IVirtualSystemDescription> copy(pNewDesc);
4615 copy.queryInterfaceTo(aDescription);
4616
4617 AutoWriteLock alock(pAppliance);
4618
4619 pAppliance->m->virtualSystemDescriptions.push_back(pNewDesc);
4620 }
4621 catch(HRESULT arc)
4622 {
4623 rc = arc;
4624 }
4625
4626 return rc;
4627}
4628
4629/* 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