VirtualBox

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

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

OVF: use capacity instead of populated size as a better approximation for progress weighting

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

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