VirtualBox

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

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

Main: Fix Compiling ApplicaneImpl on Windows

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

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