VirtualBox

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

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

Main: move vbox-independent OVF reader code to separate file; move some more string implementation from com::UTF8Str to iprt::MiniString

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette