VirtualBox

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

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

OVF: fix export breakage from IProgress update

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

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