VirtualBox

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

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

Main/Appliance: use the flattenTo API when exporting the image to streamOptimized VMDK.

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

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