VirtualBox

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

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

OVF: for now, yes, this will be improved later

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 152.7 KB
Line 
1/* $Id: ApplianceImpl.cpp 17758 2009-03-12 15:37:30Z 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 uint32_t ulType;
756 pelmItemChild->copyValue(ulType);
757 i.resourceType = (OVFResourceType_T)ulType;
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 if (!m->strPath.endsWith(".ovf", Utf8Str::CaseInsensitive))
1160 return setError(VBOX_E_FILE_ERROR,
1161 tr("Appliance file must have .ovf extension"));
1162
1163 try
1164 {
1165 xml::XmlFileParser parser;
1166 xml::Document doc;
1167 parser.read(m->strPath.raw(),
1168 doc);
1169
1170 const xml::ElementNode *pRootElem = doc.getRootElement();
1171 if (strcmp(pRootElem->getName(), "Envelope"))
1172 return setError(VBOX_E_FILE_ERROR,
1173 tr("Root element in OVF file must be \"Envelope\"."));
1174
1175 // OVF has the following rough layout:
1176 /*
1177 -- <References> .... files referenced from other parts of the file, such as VMDK images
1178 -- Metadata, comprised of several section commands
1179 -- virtual machines, either a single <VirtualSystem>, or a <VirtualSystemCollection>
1180 -- optionally <Strings> for localization
1181 */
1182
1183 // get all "File" child elements of "References" section so we can look up files easily;
1184 // first find the "References" sections so we can look up files
1185 xml::ElementNodesList listFileElements; // receives all /Envelope/References/File nodes
1186 const xml::ElementNode *pReferencesElem;
1187 if ((pReferencesElem = pRootElem->findChildElement("References")))
1188 pReferencesElem->getChildElements(listFileElements, "File");
1189
1190 // now go though the sections
1191 if (!(SUCCEEDED(rc = LoopThruSections(m->strPath.raw(), pReferencesElem, pRootElem))))
1192 return rc;
1193 }
1194 catch(xml::Error &x)
1195 {
1196 return setError(VBOX_E_FILE_ERROR,
1197 x.what());
1198 }
1199
1200 return S_OK;
1201}
1202
1203/**
1204 * Public method implementation.
1205 * @return
1206 */
1207STDMETHODIMP Appliance::Interpret()
1208{
1209 // @todo:
1210 // - don't use COM methods but the methods directly (faster, but needs appropriate locking of that objects itself (s. HardDisk))
1211 // - Appropriate handle errors like not supported file formats
1212 AutoCaller autoCaller(this);
1213 CheckComRCReturnRC(autoCaller.rc());
1214
1215 AutoWriteLock(this);
1216
1217 HRESULT rc = S_OK;
1218
1219 /* Clear any previous virtual system descriptions */
1220 m->virtualSystemDescriptions.clear();
1221
1222 /* We need the default path for storing disk images */
1223 ComPtr<ISystemProperties> systemProps;
1224 rc = mVirtualBox->COMGETTER(SystemProperties)(systemProps.asOutParam());
1225 CheckComRCReturnRC(rc);
1226 Bstr bstrDefaultHardDiskLocation;
1227 rc = systemProps->COMGETTER(DefaultHardDiskFolder)(bstrDefaultHardDiskLocation.asOutParam());
1228 CheckComRCReturnRC(rc);
1229
1230 /* Try/catch so we can clean up on error */
1231 try
1232 {
1233 list<VirtualSystem>::const_iterator it;
1234 /* Iterate through all virtual systems */
1235 for (it = m->llVirtualSystems.begin();
1236 it != m->llVirtualSystems.end();
1237 ++it)
1238 {
1239 const VirtualSystem &vsysThis = *it;
1240
1241 ComObjPtr<VirtualSystemDescription> pNewDesc;
1242 rc = pNewDesc.createObject();
1243 CheckComRCThrowRC(rc);
1244 rc = pNewDesc->init();
1245 CheckComRCThrowRC(rc);
1246
1247 /* Guest OS type */
1248 Utf8Str strOsTypeVBox,
1249 strCIMOSType = Utf8StrFmt("%RI32", (uint32_t)vsysThis.cimos);
1250 convertCIMOSType2VBoxOSType(strOsTypeVBox, vsysThis.cimos);
1251 pNewDesc->addEntry(VirtualSystemDescriptionType_OS,
1252 "",
1253 strCIMOSType,
1254 strOsTypeVBox);
1255
1256 /* VM name */
1257 /* If the there isn't any name specified create a default one out of
1258 * the OS type */
1259 Utf8Str nameVBox = vsysThis.strName;
1260 if (nameVBox.isEmpty())
1261 nameVBox = strOsTypeVBox;
1262 searchUniqueVMName(nameVBox);
1263 pNewDesc->addEntry(VirtualSystemDescriptionType_Name,
1264 "",
1265 vsysThis.strName,
1266 nameVBox);
1267
1268 /* Now that we know the OS type, get our internal defaults based on that. */
1269 ComPtr<IGuestOSType> pGuestOSType;
1270 rc = mVirtualBox->GetGuestOSType(Bstr(strOsTypeVBox), pGuestOSType.asOutParam());
1271 CheckComRCThrowRC(rc);
1272
1273 /* CPU count */
1274 ULONG cpuCountVBox = vsysThis.cCPUs;
1275 /* Check for the constrains */
1276 if (cpuCountVBox > 1) //SchemaDefs::MaxCPUCount)
1277 {
1278 pNewDesc->addWarning(tr("The virtual system claims support for %u CPU's, but VirtualBox has support for max %u CPU's only."),
1279 cpuCountVBox, 1); //SchemaDefs::MaxCPUCount);
1280 cpuCountVBox = 1; //SchemaDefs::MaxCPUCount;
1281 }
1282 if (vsysThis.cCPUs == 0)
1283 cpuCountVBox = 1;
1284 pNewDesc->addEntry(VirtualSystemDescriptionType_CPU,
1285 "",
1286 Utf8StrFmt("%RI32", (uint32_t)vsysThis.cCPUs),
1287 Utf8StrFmt("%RI32", (uint32_t)cpuCountVBox));
1288
1289 /* RAM */
1290 uint64_t ullMemSizeVBox = vsysThis.ullMemorySize / _1M;
1291 /* Check for the constrains */
1292 if (ullMemSizeVBox != 0 &&
1293 (ullMemSizeVBox < static_cast<uint64_t>(SchemaDefs::MinGuestRAM) ||
1294 ullMemSizeVBox > static_cast<uint64_t>(SchemaDefs::MaxGuestRAM)))
1295 {
1296 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."),
1297 ullMemSizeVBox, SchemaDefs::MinGuestRAM, SchemaDefs::MaxGuestRAM);
1298 ullMemSizeVBox = RT_MIN(RT_MAX(ullMemSizeVBox, static_cast<uint64_t>(SchemaDefs::MinGuestRAM)), static_cast<uint64_t>(SchemaDefs::MaxGuestRAM));
1299 }
1300 if (vsysThis.ullMemorySize == 0)
1301 {
1302 /* If the RAM of the OVF is zero, use our predefined values */
1303 ULONG memSizeVBox2;
1304 rc = pGuestOSType->COMGETTER(RecommendedRAM)(&memSizeVBox2);
1305 CheckComRCThrowRC(rc);
1306 /* VBox stores that in MByte */
1307 ullMemSizeVBox = (uint64_t)memSizeVBox2;
1308 }
1309 pNewDesc->addEntry(VirtualSystemDescriptionType_Memory,
1310 "",
1311 Utf8StrFmt("%RI64", (uint64_t)vsysThis.ullMemorySize),
1312 Utf8StrFmt("%RI64", (uint64_t)ullMemSizeVBox));
1313
1314 /* Audio */
1315 if (!vsysThis.strSoundCardType.isNull())
1316 /* Currently we set the AC97 always.
1317 @todo: figure out the hardware which could be possible */
1318 pNewDesc->addEntry(VirtualSystemDescriptionType_SoundCard,
1319 "",
1320 vsysThis.strSoundCardType,
1321 Utf8StrFmt("%RI32", (uint32_t)AudioControllerType_AC97));
1322
1323#ifdef VBOX_WITH_USB
1324 /* USB Controller */
1325 if (vsysThis.fHasUsbController)
1326 pNewDesc->addEntry(VirtualSystemDescriptionType_USBController, "", "", "");
1327#endif /* VBOX_WITH_USB */
1328
1329 /* Network Controller */
1330 // @todo: there is no hardware specification in the OVF file; supposedly the
1331 // hardware will then be determined by the VirtualSystemType element (e.g. "vmx-07")
1332 if (vsysThis.llNetworkNames.size() > 0)
1333 {
1334 /* Check for the constrains */
1335 if (vsysThis.llNetworkNames.size() > SchemaDefs::NetworkAdapterCount)
1336 pNewDesc->addWarning(tr("The virtual system claims support for %u network adapters, but VirtualBox has support for max %u network adapter only."),
1337 vsysThis.llNetworkNames.size(), SchemaDefs::NetworkAdapterCount);
1338
1339 /* Get the default network adapter type for the selected guest OS */
1340 NetworkAdapterType_T nwAdapterVBox = NetworkAdapterType_Am79C970A;
1341 rc = pGuestOSType->COMGETTER(AdapterType)(&nwAdapterVBox);
1342 CheckComRCThrowRC(rc);
1343 list<Utf8Str>::const_iterator nwIt;
1344 /* Iterate through all abstract networks. We support 8 network
1345 * adapters at the maximum, so the first 8 will be added only. */
1346 size_t a = 0;
1347 for (nwIt = vsysThis.llNetworkNames.begin();
1348 nwIt != vsysThis.llNetworkNames.end() && a < SchemaDefs::NetworkAdapterCount;
1349 ++nwIt, ++a)
1350 {
1351 Utf8Str strNetwork = *nwIt; // logical network to connect to
1352 // make sure it's one of these two
1353 if ( (strNetwork.compare("Null", Utf8Str::CaseInsensitive))
1354 && (strNetwork.compare("Bridged", Utf8Str::CaseInsensitive))
1355 && (strNetwork.compare("Internal", Utf8Str::CaseInsensitive))
1356 && (strNetwork.compare("HostOnly", Utf8Str::CaseInsensitive))
1357 )
1358 strNetwork = "NAT";
1359
1360 pNewDesc->addEntry(VirtualSystemDescriptionType_NetworkAdapter,
1361 "", // ref
1362 strNetwork, // orig
1363 Utf8StrFmt("%RI32", (uint32_t)nwAdapterVBox), // conf
1364 Utf8StrFmt("type=%s", strNetwork.c_str())); // extra conf
1365 }
1366 }
1367
1368 /* Floppy Drive */
1369 if (vsysThis.fHasFloppyDrive)
1370 pNewDesc->addEntry(VirtualSystemDescriptionType_Floppy, "", "", "");
1371
1372 /* CD Drive */
1373 /* @todo: I can't disable the CDROM. So nothing to do for now */
1374 /*
1375 if (vsysThis.fHasCdromDrive)
1376 pNewDesc->addEntry(VirtualSystemDescriptionType_CDROM, "", "", "");*/
1377
1378 /* Hard disk Controller */
1379 uint16_t cIDEused = 0;
1380 uint16_t cSATAused = 0;
1381 uint16_t cSCSIused = 0;
1382 ControllersMap::const_iterator hdcIt;
1383 /* Iterate through all hard disk controllers */
1384 for (hdcIt = vsysThis.mapControllers.begin();
1385 hdcIt != vsysThis.mapControllers.end();
1386 ++hdcIt)
1387 {
1388 const HardDiskController &hdc = hdcIt->second;
1389 Utf8Str strControllerID = Utf8StrFmt("%RI32", (uint32_t)hdc.idController);
1390
1391 switch (hdc.system)
1392 {
1393 case HardDiskController::IDE:
1394 {
1395 /* Check for the constrains */
1396 /* @todo: I'm very confused! Are these bits *one* controller or
1397 is every port/bus declared as an extra controller. */
1398 if (cIDEused < 4)
1399 {
1400 // @todo: figure out the IDE types
1401 /* Use PIIX4 as default */
1402 Utf8Str strType = "PIIX4";
1403 if (!RTStrICmp(hdc.strControllerType.c_str(), "PIIX3"))
1404 strType = "PIIX3";
1405 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerIDE,
1406 strControllerID,
1407 hdc.strControllerType,
1408 strType);
1409 }
1410 else
1411 {
1412 /* Warn only once */
1413 if (cIDEused == 1)
1414 pNewDesc->addWarning(tr("The virtual system claims support for more than one IDE controller, but VirtualBox has support for only one."));
1415
1416 }
1417 ++cIDEused;
1418 break;
1419 }
1420
1421#ifdef VBOX_WITH_AHCI
1422 case HardDiskController::SATA:
1423 {
1424 /* Check for the constrains */
1425 if (cSATAused < 1)
1426 {
1427 // @todo: figure out the SATA types
1428 /* We only support a plain AHCI controller, so use them always */
1429 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSATA,
1430 strControllerID,
1431 hdc.strControllerType,
1432 "AHCI");
1433 }
1434 else
1435 {
1436 /* Warn only once */
1437 if (cSATAused == 1)
1438 pNewDesc->addWarning(tr("The virtual system claims support for more than one SATA controller, but VirtualBox has support for only one."));
1439
1440 }
1441 ++cSATAused;
1442 break;
1443 }
1444#endif /* VBOX_WITH_AHCI */
1445
1446 case HardDiskController::SCSI:
1447 {
1448 /* Check for the constrains */
1449 if (cSCSIused < 1)
1450 {
1451 Utf8Str hdcController = "LsiLogic";
1452 if (!RTStrICmp(hdc.strControllerType.c_str(), "BusLogic"))
1453 hdcController = "BusLogic";
1454 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSCSI,
1455 strControllerID,
1456 hdc.strControllerType,
1457 hdcController);
1458 }
1459 else
1460 {
1461 /* Warn only once */
1462 if (cSCSIused == 1)
1463 pNewDesc->addWarning(tr("The virtual system claims support for more than one SCSI controller, but VirtualBox has support for only one."));
1464
1465 }
1466 ++cSCSIused;
1467 break;
1468 }
1469 }
1470 }
1471
1472 /* Hard disks */
1473 if (vsysThis.mapVirtualDisks.size() > 0)
1474 {
1475 VirtualDisksMap::const_iterator itVD;
1476 /* Iterate through all hard disks ()*/
1477 for (itVD = vsysThis.mapVirtualDisks.begin();
1478 itVD != vsysThis.mapVirtualDisks.end();
1479 ++itVD)
1480 {
1481 const VirtualDisk &hd = itVD->second;
1482 /* Get the associated disk image */
1483 const DiskImage &di = m->mapDisks[hd.strDiskId];
1484
1485 // @todo:
1486 // - figure out all possible vmdk formats we also support
1487 // - figure out if there is a url specifier for vhd already
1488 // - we need a url specifier for the vdi format
1489 if ( (!RTStrICmp(di.strFormat.c_str(), "http://www.vmware.com/specifications/vmdk.html#sparse"))
1490 || (!RTStrICmp(di.strFormat.c_str(), "http://www.vmware.com/specifications/vmdk.html#compressed"))
1491 )
1492 {
1493 /* If the href is empty use the VM name as filename */
1494 Utf8Str strFilename = di.strHref;
1495 if (!strFilename.length())
1496 strFilename = Utf8StrFmt("%s.vmdk", nameVBox.c_str());
1497 /* Construct a unique target path */
1498 Utf8StrFmt strPath("%ls%c%s",
1499 bstrDefaultHardDiskLocation.raw(),
1500 RTPATH_DELIMITER,
1501 strFilename.c_str());
1502 searchUniqueDiskImageFilePath(strPath);
1503
1504 /* find the description for the hard disk controller
1505 * that has the same ID as hd.idController */
1506 const VirtualSystemDescriptionEntry *pController;
1507 if (!(pController = pNewDesc->findControllerFromID(hd.idController)))
1508 throw setError(E_FAIL,
1509 tr("Internal inconsistency looking up hard disk controller."));
1510
1511 /* controller to attach to, and the bus within that controller */
1512 Utf8StrFmt strExtraConfig("controller=%RI16;channel=%RI16",
1513 pController->ulIndex,
1514 hd.ulAddressOnParent);
1515 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskImage,
1516 hd.strDiskId,
1517 di.strHref,
1518 strPath,
1519 strExtraConfig);
1520 }
1521 else
1522 throw setError(VBOX_E_FILE_ERROR,
1523 tr("Unsupported format for virtual disk image in OVF: \"%s\"", di.strFormat.c_str()));
1524 }
1525 }
1526
1527 m->virtualSystemDescriptions.push_back(pNewDesc);
1528 }
1529 }
1530 catch (HRESULT aRC)
1531 {
1532 /* On error we clear the list & return */
1533 m->virtualSystemDescriptions.clear();
1534 rc = aRC;
1535 }
1536
1537 return rc;
1538}
1539
1540/**
1541 * Public method implementation.
1542 * @param aProgress
1543 * @return
1544 */
1545STDMETHODIMP Appliance::ImportMachines(IProgress **aProgress)
1546{
1547 CheckComArgOutPointerValid(aProgress);
1548
1549 AutoCaller autoCaller(this);
1550 CheckComRCReturnRC(autoCaller.rc());
1551
1552 AutoReadLock(this);
1553
1554 HRESULT rc = S_OK;
1555
1556 ComObjPtr<Progress> progress;
1557 try
1558 {
1559 uint32_t opCount = calcMaxProgress();
1560 Bstr progressDesc = BstrFmt(tr("Import appliance '%s'"),
1561 m->strPath.raw());
1562 /* Create the progress object */
1563 progress.createObject();
1564 rc = progress->init(mVirtualBox, static_cast<IAppliance*>(this),
1565 progressDesc,
1566 FALSE /* aCancelable */,
1567 opCount,
1568 progressDesc);
1569 if (FAILED(rc)) throw rc;
1570
1571 /* Initialize our worker task */
1572 std::auto_ptr<TaskImportMachines> task(new TaskImportMachines(this, progress));
1573 //AssertComRCThrowRC (task->autoCaller.rc());
1574
1575 rc = task->startThread();
1576 if (FAILED(rc)) throw rc;
1577
1578 task.release();
1579 }
1580 catch (HRESULT aRC)
1581 {
1582 rc = aRC;
1583 }
1584
1585 if (SUCCEEDED(rc))
1586 /* Return progress to the caller */
1587 progress.queryInterfaceTo(aProgress);
1588
1589 return rc;
1590}
1591
1592STDMETHODIMP Appliance::Write(IN_BSTR path, IProgress **aProgress)
1593{
1594 HRESULT rc = S_OK;
1595
1596 CheckComArgOutPointerValid(aProgress);
1597
1598 AutoCaller autoCaller(this);
1599 if (FAILED(rc = autoCaller.rc())) return rc;
1600
1601 AutoWriteLock(this);
1602
1603 // see if we can handle this file; for now we insist it has an ".ovf" extension
1604 m->strPath = path;
1605 if (!m->strPath.endsWith(".ovf", Utf8Str::CaseInsensitive))
1606 return setError(VBOX_E_FILE_ERROR,
1607 tr("Appliance file must have .ovf extension"));
1608
1609 ComObjPtr<Progress> progress;
1610 try
1611 {
1612 uint32_t opCount = calcMaxProgress();
1613 Bstr progressDesc = BstrFmt(tr("Write appliance '%s'"),
1614 m->strPath.raw());
1615 /* Create the progress object */
1616 progress.createObject();
1617 rc = progress->init(mVirtualBox, static_cast<IAppliance*>(this),
1618 progressDesc,
1619 FALSE /* aCancelable */,
1620 opCount,
1621 progressDesc);
1622 CheckComRCThrowRC(rc);
1623
1624 /* Initialize our worker task */
1625 std::auto_ptr<TaskWriteOVF> task(new TaskWriteOVF(this, progress));
1626 //AssertComRCThrowRC (task->autoCaller.rc());
1627
1628 rc = task->startThread();
1629 CheckComRCThrowRC(rc);
1630
1631 task.release();
1632 }
1633 catch (HRESULT aRC)
1634 {
1635 rc = aRC;
1636 }
1637
1638 if (SUCCEEDED(rc))
1639 /* Return progress to the caller */
1640 progress.queryInterfaceTo(aProgress);
1641
1642 return rc;
1643}
1644
1645HRESULT Appliance::searchUniqueVMName(Utf8Str& aName) const
1646{
1647 IMachine *machine = NULL;
1648 char *tmpName = RTStrDup(aName.c_str());
1649 int i = 1;
1650 /* @todo: Maybe too cost-intensive; try to find a lighter way */
1651 while (mVirtualBox->FindMachine(Bstr(tmpName), &machine) != VBOX_E_OBJECT_NOT_FOUND)
1652 {
1653 RTStrFree(tmpName);
1654 RTStrAPrintf(&tmpName, "%s_%d", aName.c_str(), i);
1655 ++i;
1656 }
1657 aName = tmpName;
1658 RTStrFree(tmpName);
1659
1660 return S_OK;
1661}
1662
1663HRESULT Appliance::searchUniqueDiskImageFilePath(Utf8Str& aName) const
1664{
1665 IHardDisk *harddisk = NULL;
1666 char *tmpName = RTStrDup(aName.c_str());
1667 int i = 1;
1668 /* Check if the file exists or if a file with this path is registered
1669 * already */
1670 /* @todo: Maybe too cost-intensive; try to find a lighter way */
1671 while (RTPathExists(tmpName) ||
1672 mVirtualBox->FindHardDisk(Bstr(tmpName), &harddisk) != VBOX_E_OBJECT_NOT_FOUND)
1673 {
1674 RTStrFree(tmpName);
1675 char *tmpDir = RTStrDup(aName.c_str());
1676 RTPathStripFilename(tmpDir);;
1677 char *tmpFile = RTStrDup(RTPathFilename(aName.c_str()));
1678 RTPathStripExt(tmpFile);
1679 const char *tmpExt = RTPathExt(aName.c_str());
1680 RTStrAPrintf(&tmpName, "%s%c%s_%d%s", tmpDir, RTPATH_DELIMITER, tmpFile, i, tmpExt);
1681 RTStrFree(tmpFile);
1682 RTStrFree(tmpDir);
1683 ++i;
1684 }
1685 aName = tmpName;
1686 RTStrFree(tmpName);
1687
1688 return S_OK;
1689}
1690
1691/**
1692 * Calculates the maximum progress value for importMachines() and write().
1693 * @return
1694 */
1695uint32_t Appliance::calcMaxProgress()
1696{
1697 /* Figure out how many sub operation the import will need */
1698 /* One for the appliance */
1699 uint32_t opCount = 1;
1700 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;
1701 for (it = m->virtualSystemDescriptions.begin();
1702 it != m->virtualSystemDescriptions.end();
1703 ++it)
1704 {
1705 /* One for every Virtual System */
1706 ++opCount;
1707 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);
1708 /* One for every hard disk of the Virtual System */
1709 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
1710 opCount += (uint32_t)avsdeHDs.size();
1711 }
1712
1713 return opCount;
1714}
1715
1716struct MyHardDiskAttachment
1717{
1718 Guid uuid;
1719 ComPtr<IMachine> pMachine;
1720 Bstr controllerType;
1721 int32_t lChannel;
1722 int32_t lDevice;
1723};
1724
1725/**
1726 * Worker thread implementation for ImportMachines().
1727 * @param aThread
1728 * @param pvUser
1729 */
1730/* static */
1731DECLCALLBACK(int) Appliance::taskThreadImportMachines(RTTHREAD aThread, void *pvUser)
1732{
1733 std::auto_ptr<TaskImportMachines> task(static_cast<TaskImportMachines*>(pvUser));
1734 AssertReturn(task.get(), VERR_GENERAL_FAILURE);
1735
1736 Appliance *pAppliance = task->pAppliance;
1737
1738 LogFlowFuncEnter();
1739 LogFlowFunc(("Appliance %p\n", pAppliance));
1740
1741 AutoCaller autoCaller(pAppliance);
1742 CheckComRCReturnRC(autoCaller.rc());
1743
1744 AutoWriteLock appLock(pAppliance);
1745
1746 HRESULT rc = S_OK;
1747
1748 ComPtr<IVirtualBox> pVirtualBox(pAppliance->mVirtualBox);
1749
1750 // rollback for errors:
1751 // 1) a list of images that we created/imported
1752 list<MyHardDiskAttachment> llHardDiskAttachments;
1753 list< ComPtr<IHardDisk> > llHardDisksCreated;
1754 list<Guid> llMachinesRegistered;
1755
1756 ComPtr<ISession> session;
1757 bool fSessionOpen = false;
1758 rc = session.createInprocObject(CLSID_Session);
1759 CheckComRCReturnRC(rc);
1760
1761 list<VirtualSystem>::const_iterator it;
1762 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it1;
1763 /* Iterate through all virtual systems of that appliance */
1764 size_t i = 0;
1765 for (it = pAppliance->m->llVirtualSystems.begin(),
1766 it1 = pAppliance->m->virtualSystemDescriptions.begin();
1767 it != pAppliance->m->llVirtualSystems.end();
1768 ++it, ++it1, ++i)
1769 {
1770 const VirtualSystem &vsysThis = *it;
1771 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it1);
1772
1773 ComPtr<IMachine> pNewMachine;
1774
1775 /* Catch possible errors */
1776 try
1777 {
1778 if (!task->progress.isNull())
1779 task->progress->advanceOperation(BstrFmt(tr("Importing Virtual System %d"), i + 1));
1780
1781 /* How many sub notifications are necessary? */
1782 const float opCountMax = 100.0/5;
1783 uint32_t opCount = 0;
1784
1785 /* Guest OS type */
1786 std::list<VirtualSystemDescriptionEntry*> vsdeOS;
1787 vsdeOS = vsdescThis->findByType(VirtualSystemDescriptionType_OS);
1788 if (vsdeOS.size() < 1)
1789 throw setError(VBOX_E_FILE_ERROR,
1790 tr("Missing guest OS type"));
1791 const Utf8Str &strOsTypeVBox = vsdeOS.front()->strVbox;
1792
1793 /* Now that we know the base system get our internal defaults based on that. */
1794 ComPtr<IGuestOSType> osType;
1795 rc = pVirtualBox->GetGuestOSType(Bstr(strOsTypeVBox), osType.asOutParam());
1796 if (FAILED(rc)) throw rc;
1797
1798 /* Create the machine */
1799 /* First get the name */
1800 std::list<VirtualSystemDescriptionEntry*> vsdeName = vsdescThis->findByType(VirtualSystemDescriptionType_Name);
1801 if (vsdeName.size() < 1)
1802 throw setError(VBOX_E_FILE_ERROR,
1803 tr("Missing VM name"));
1804 const Utf8Str &strNameVBox = vsdeName.front()->strVbox;
1805 rc = pVirtualBox->CreateMachine(Bstr(strNameVBox), Bstr(strOsTypeVBox),
1806 Bstr(), Guid(),
1807 pNewMachine.asOutParam());
1808 if (FAILED(rc)) throw rc;
1809
1810 if (!task->progress.isNull())
1811 rc = task->progress->notifyProgress((uint32_t)(opCountMax * opCount++));
1812
1813 /* CPU count (ignored for now) */
1814 // EntriesList vsdeCPU = vsd->findByType (VirtualSystemDescriptionType_CPU);
1815
1816 /* RAM */
1817 std::list<VirtualSystemDescriptionEntry*> vsdeRAM = vsdescThis->findByType(VirtualSystemDescriptionType_Memory);
1818 ComAssertMsgThrow(vsdeRAM.size() == 1, ("RAM size missing"), E_FAIL);
1819 const Utf8Str &memoryVBox = vsdeRAM.front()->strVbox;
1820 ULONG tt = (ULONG)RTStrToUInt64(memoryVBox.c_str());
1821 rc = pNewMachine->COMSETTER(MemorySize)(tt);
1822 if (FAILED(rc)) throw rc;
1823
1824 /* VRAM */
1825 /* Get the recommended VRAM for this guest OS type */
1826 ULONG vramVBox;
1827 rc = osType->COMGETTER(RecommendedVRAM)(&vramVBox);
1828 if (FAILED(rc)) throw rc;
1829
1830 /* Set the VRAM */
1831 rc = pNewMachine->COMSETTER(VRAMSize)(vramVBox);
1832 if (FAILED(rc)) throw rc;
1833
1834 if (!task->progress.isNull())
1835 task->progress->notifyProgress((uint32_t)(opCountMax * opCount++));
1836
1837 /* Audio Adapter */
1838 std::list<VirtualSystemDescriptionEntry*> vsdeAudioAdapter = vsdescThis->findByType(VirtualSystemDescriptionType_SoundCard);
1839 /* @todo: we support one audio adapter only */
1840 if (vsdeAudioAdapter.size() > 0)
1841 {
1842 const Utf8Str& audioAdapterVBox = vsdeAudioAdapter.front()->strVbox;
1843 if (RTStrICmp(audioAdapterVBox, "null") != 0)
1844 {
1845 uint32_t audio = RTStrToUInt32(audioAdapterVBox.c_str());
1846 ComPtr<IAudioAdapter> audioAdapter;
1847 rc = pNewMachine->COMGETTER(AudioAdapter)(audioAdapter.asOutParam());
1848 if (FAILED(rc)) throw rc;
1849 rc = audioAdapter->COMSETTER(Enabled)(true);
1850 if (FAILED(rc)) throw rc;
1851 rc = audioAdapter->COMSETTER(AudioController)(static_cast<AudioControllerType_T>(audio));
1852 if (FAILED(rc)) throw rc;
1853 }
1854 }
1855
1856#ifdef VBOX_WITH_USB
1857 /* USB Controller */
1858 std::list<VirtualSystemDescriptionEntry*> vsdeUSBController = vsdescThis->findByType(VirtualSystemDescriptionType_USBController);
1859 // USB support is enabled if there's at least one such entry; to disable USB support,
1860 // the type of the USB item would have been changed to "ignore"
1861 bool fUSBEnabled = vsdeUSBController.size() > 0;
1862
1863 ComPtr<IUSBController> usbController;
1864 rc = pNewMachine->COMGETTER(USBController)(usbController.asOutParam());
1865 if (FAILED(rc)) throw rc;
1866 rc = usbController->COMSETTER(Enabled)(fUSBEnabled);
1867 if (FAILED(rc)) throw rc;
1868#endif /* VBOX_WITH_USB */
1869
1870 if (!task->progress.isNull())
1871 task->progress->notifyProgress((uint32_t)(opCountMax * opCount++));
1872
1873 /* Change the network adapters */
1874 std::list<VirtualSystemDescriptionEntry*> vsdeNW = vsdescThis->findByType(VirtualSystemDescriptionType_NetworkAdapter);
1875 if (vsdeNW.size() == 0)
1876 {
1877 /* No network adapters, so we have to disable our default one */
1878 ComPtr<INetworkAdapter> nwVBox;
1879 rc = pNewMachine->GetNetworkAdapter(0, nwVBox.asOutParam());
1880 if (FAILED(rc)) throw rc;
1881 rc = nwVBox->COMSETTER(Enabled)(false);
1882 if (FAILED(rc)) throw rc;
1883 }
1884 else
1885 {
1886 list<VirtualSystemDescriptionEntry*>::const_iterator nwIt;
1887 /* Iterate through all network cards. We support 8 network adapters
1888 * at the maximum. (@todo: warn if there are more!) */
1889 size_t a = 0;
1890 for (nwIt = vsdeNW.begin();
1891 (nwIt != vsdeNW.end() && a < SchemaDefs::NetworkAdapterCount);
1892 ++nwIt, ++a)
1893 {
1894 const VirtualSystemDescriptionEntry* pvsys = *nwIt;
1895
1896 const Utf8Str &nwTypeVBox = pvsys->strVbox;
1897 uint32_t tt1 = RTStrToUInt32(nwTypeVBox.c_str());
1898 ComPtr<INetworkAdapter> pNetworkAdapter;
1899 rc = pNewMachine->GetNetworkAdapter((ULONG)a, pNetworkAdapter.asOutParam());
1900 if (FAILED(rc)) throw rc;
1901 /* Enable the network card & set the adapter type */
1902 rc = pNetworkAdapter->COMSETTER(Enabled)(true);
1903 if (FAILED(rc)) throw rc;
1904 rc = pNetworkAdapter->COMSETTER(AdapterType)(static_cast<NetworkAdapterType_T>(tt1));
1905 if (FAILED(rc)) throw rc;
1906
1907 // default is NAT; change to "bridged" if extra conf says so
1908 if (!pvsys->strExtraConfig.compare("type=Bridged", Utf8Str::CaseInsensitive))
1909 {
1910 rc = pNetworkAdapter->AttachToBridgedInterface();
1911 if (FAILED(rc)) throw rc;
1912 }
1913 }
1914 }
1915
1916 /* Floppy drive */
1917 std::list<VirtualSystemDescriptionEntry*> vsdeFloppy = vsdescThis->findByType(VirtualSystemDescriptionType_Floppy);
1918 // Floppy support is enabled if there's at least one such entry; to disable floppy support,
1919 // the type of the floppy item would have been changed to "ignore"
1920 bool fFloppyEnabled = vsdeFloppy.size() > 0;
1921 ComPtr<IFloppyDrive> floppyDrive;
1922 rc = pNewMachine->COMGETTER(FloppyDrive)(floppyDrive.asOutParam());
1923 if (FAILED(rc)) throw rc;
1924 rc = floppyDrive->COMSETTER(Enabled)(fFloppyEnabled);
1925 if (FAILED(rc)) throw rc;
1926
1927 if (!task->progress.isNull())
1928 task->progress->notifyProgress((uint32_t)(opCountMax * opCount++));
1929
1930 /* CDROM drive */
1931 /* @todo: I can't disable the CDROM. So nothing to do for now */
1932 // std::list<VirtualSystemDescriptionEntry*> vsdeFloppy = vsd->findByType(VirtualSystemDescriptionType_CDROM);
1933
1934 /* Hard disk controller IDE */
1935 std::list<VirtualSystemDescriptionEntry*> vsdeHDCIDE = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerIDE);
1936 if (vsdeHDCIDE.size() > 1)
1937 throw setError(VBOX_E_FILE_ERROR,
1938 tr("Too many IDE controllers in OVF; VirtualBox only supports one"));
1939 if (vsdeHDCIDE.size() == 1)
1940 {
1941 ComPtr<IStorageController> pController;
1942 rc = pNewMachine->GetStorageControllerByName(Bstr("IDE"), pController.asOutParam());
1943 if (FAILED(rc)) throw rc;
1944
1945 const char *pcszIDEType = vsdeHDCIDE.front()->strVbox.c_str();
1946 if (!strcmp(pcszIDEType, "PIIX3"))
1947 rc = pController->COMSETTER(ControllerType)(StorageControllerType_PIIX3);
1948 else if (!strcmp(pcszIDEType, "PIIX4"))
1949 rc = pController->COMSETTER(ControllerType)(StorageControllerType_PIIX4);
1950 else if (!strcmp(pcszIDEType, "ICH6"))
1951 rc = pController->COMSETTER(ControllerType)(StorageControllerType_ICH6);
1952 else
1953 throw setError(VBOX_E_FILE_ERROR,
1954 tr("Invalid IDE controller type \"%s\""),
1955 pcszIDEType);
1956 if (FAILED(rc)) throw rc;
1957 }
1958#ifdef VBOX_WITH_AHCI
1959 /* Hard disk controller SATA */
1960 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSATA = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSATA);
1961 if (vsdeHDCSATA.size() > 1)
1962 throw setError(VBOX_E_FILE_ERROR,
1963 tr("Too many SATA controllers in OVF; VirtualBox only supports one"));
1964 if (vsdeHDCSATA.size() > 0)
1965 {
1966 ComPtr<IStorageController> pController;
1967 const Utf8Str &hdcVBox = vsdeHDCSATA.front()->strVbox;
1968 if (hdcVBox == "AHCI")
1969 {
1970 rc = pNewMachine->AddStorageController(Bstr("SATA"), StorageBus_SATA, pController.asOutParam());
1971 if (FAILED(rc)) throw rc;
1972 }
1973 else
1974 throw setError(VBOX_E_FILE_ERROR,
1975 tr("Invalid SATA controller type \"%s\""),
1976 hdcVBox.c_str());
1977 }
1978#endif /* VBOX_WITH_AHCI */
1979
1980 /* Hard disk controller SCSI */
1981 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSCSI = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSCSI);
1982 if (vsdeHDCSATA.size() > 1)
1983 throw setError(VBOX_E_FILE_ERROR,
1984 tr("Too many SCSI controllers in OVF; VirtualBox only supports one"));
1985 if (vsdeHDCSCSI.size() > 0)
1986 {
1987 ComPtr<IStorageController> pController;
1988 StorageControllerType_T controllerType;
1989 const Utf8Str &hdcVBox = vsdeHDCSCSI.front()->strVbox;
1990 if (hdcVBox == "LsiLogic")
1991 controllerType = StorageControllerType_LsiLogic;
1992 else if (hdcVBox == "BusLogic")
1993 controllerType = StorageControllerType_BusLogic;
1994 else
1995 throw setError(VBOX_E_FILE_ERROR,
1996 tr("Invalid SCSI controller type \"%s\""),
1997 hdcVBox.c_str());
1998
1999 rc = pNewMachine->AddStorageController(Bstr("SCSI"), StorageBus_SCSI, pController.asOutParam());
2000 if (FAILED(rc)) throw rc;
2001 rc = pController->COMSETTER(ControllerType)(controllerType);
2002 if (FAILED(rc)) throw rc;
2003 }
2004
2005 /* Now its time to register the machine before we add any hard disks */
2006 rc = pVirtualBox->RegisterMachine(pNewMachine);
2007 if (FAILED(rc)) throw rc;
2008
2009 Guid newMachineId;
2010 rc = pNewMachine->COMGETTER(Id)(newMachineId.asOutParam());
2011 if (FAILED(rc)) throw rc;
2012
2013 if (!task->progress.isNull())
2014 task->progress->notifyProgress((uint32_t)(opCountMax * opCount++));
2015
2016 // store new machine for roll-back in case of errors
2017 llMachinesRegistered.push_back(newMachineId);
2018
2019 /* Create the hard disks & connect them to the appropriate controllers. */
2020 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
2021 if (avsdeHDs.size() > 0)
2022 {
2023 /* If in the next block an error occur we have to deregister
2024 the machine, so make an extra try/catch block. */
2025 ComPtr<IHardDisk> srcHdVBox;
2026 bool fSourceHdNeedsClosing = false;
2027
2028 try
2029 {
2030 /* In order to attach hard disks we need to open a session
2031 * for the new machine */
2032 rc = pVirtualBox->OpenSession(session, newMachineId);
2033 if (FAILED(rc)) throw rc;
2034 fSessionOpen = true;
2035
2036 /* The disk image has to be on the same place as the OVF file. So
2037 * strip the filename out of the full file path. */
2038 Utf8Str strSrcDir = stripFilename(pAppliance->m->strPath);
2039
2040 /* Iterate over all given disk images */
2041 list<VirtualSystemDescriptionEntry*>::const_iterator itHD;
2042 for (itHD = avsdeHDs.begin();
2043 itHD != avsdeHDs.end();
2044 ++itHD)
2045 {
2046 VirtualSystemDescriptionEntry *vsdeHD = *itHD;
2047
2048 const char *pcszDstFilePath = vsdeHD->strVbox.c_str();
2049 /* Check if the destination file exists already or the
2050 * destination path is empty. */
2051 if ( !(*pcszDstFilePath)
2052 || RTPathExists(pcszDstFilePath)
2053 )
2054 /* This isn't allowed */
2055 throw setError(VBOX_E_FILE_ERROR,
2056 tr("Destination file '%s' exists",
2057 pcszDstFilePath));
2058
2059 /* Find the disk from the OVF's disk list */
2060 DiskImagesMap::const_iterator itDiskImage = pAppliance->m->mapDisks.find(vsdeHD->strRef);
2061 /* vsdeHD->strRef contains the disk identifier (e.g. "vmdisk1"), which should exist
2062 in the virtual system's disks map under that ID and also in the global images map. */
2063 VirtualDisksMap::const_iterator itVirtualDisk = vsysThis.mapVirtualDisks.find(vsdeHD->strRef);
2064
2065 if ( itDiskImage == pAppliance->m->mapDisks.end()
2066 || itVirtualDisk == vsysThis.mapVirtualDisks.end()
2067 )
2068 throw setError(E_FAIL,
2069 tr("Internal inconsistency looking up disk images."));
2070
2071 const DiskImage &di = itDiskImage->second;
2072 const VirtualDisk &vd = itVirtualDisk->second;
2073
2074 /* Make sure all target directories exists */
2075 rc = VirtualBox::ensureFilePathExists(pcszDstFilePath);
2076 if (FAILED(rc))
2077 throw rc;
2078
2079 ComPtr<IProgress> progress;
2080
2081 ComPtr<IHardDisk> dstHdVBox;
2082 /* If strHref is empty we have to create a new file */
2083 if (di.strHref.isEmpty())
2084 {
2085 /* Which format to use? */
2086 Bstr srcFormat = L"VDI";
2087 if ( (!RTStrICmp(di.strFormat.c_str(), "http://www.vmware.com/specifications/vmdk.html#sparse"))
2088 || (!RTStrICmp(di.strFormat.c_str(), "http://www.vmware.com/specifications/vmdk.html#compressed")))
2089 srcFormat = L"VMDK";
2090 /* Create an empty hard disk */
2091 rc = pVirtualBox->CreateHardDisk(srcFormat, Bstr(pcszDstFilePath), dstHdVBox.asOutParam());
2092 if (FAILED(rc)) throw rc;
2093
2094 /* Create a dynamic growing disk image with the given capacity */
2095 rc = dstHdVBox->CreateDynamicStorage(di.iCapacity / _1M, progress.asOutParam());
2096 if (FAILED(rc)) throw rc;
2097
2098 /* Advance to the next operation */
2099 if (!task->progress.isNull())
2100 task->progress->advanceOperation (BstrFmt(tr("Creating virtual disk image '%s'"), pcszDstFilePath));
2101 }
2102 else
2103 {
2104 /* Construct the source file path */
2105 Utf8StrFmt strSrcFilePath("%s%c%s", strSrcDir.c_str(), RTPATH_DELIMITER, di.strHref.c_str());
2106 /* Check if the source file exists */
2107 if (!RTPathExists(strSrcFilePath.c_str()))
2108 /* This isn't allowed */
2109 throw setError(VBOX_E_FILE_ERROR,
2110 tr("Source virtual disk image file '%s' doesn't exist"),
2111 strSrcFilePath.c_str());
2112
2113 /* Clone the disk image (this is necessary cause the id has
2114 * to be recreated for the case the same hard disk is
2115 * attached already from a previous import) */
2116
2117 /* First open the existing disk image */
2118 rc = pVirtualBox->OpenHardDisk(Bstr(strSrcFilePath), srcHdVBox.asOutParam());
2119 if (FAILED(rc)) throw rc;
2120 fSourceHdNeedsClosing = true;
2121
2122 /* We need the format description of the source disk image */
2123 Bstr srcFormat;
2124 rc = srcHdVBox->COMGETTER(Format)(srcFormat.asOutParam());
2125 if (FAILED(rc)) throw rc;
2126 /* Create a new hard disk interface for the destination disk image */
2127 rc = pVirtualBox->CreateHardDisk(srcFormat, Bstr(pcszDstFilePath), dstHdVBox.asOutParam());
2128 if (FAILED(rc)) throw rc;
2129 /* Clone the source disk image */
2130 rc = srcHdVBox->CloneTo(dstHdVBox, progress.asOutParam());
2131 if (FAILED(rc)) throw rc;
2132
2133 /* Advance to the next operation */
2134 if (!task->progress.isNull())
2135 task->progress->advanceOperation (BstrFmt(tr("Importing virtual disk image '%s'"), strSrcFilePath.c_str()));
2136 }
2137
2138 // now loop until the asynchronous operation completes and then
2139 // report its result
2140 BOOL fCompleted;
2141 LONG currentPercent;
2142 while (SUCCEEDED(progress->COMGETTER(Completed(&fCompleted))))
2143 {
2144 rc = progress->COMGETTER(Percent(&currentPercent));
2145 if (FAILED(rc)) throw rc;
2146 if (!task->progress.isNull())
2147 task->progress->notifyProgress(currentPercent);
2148 if (fCompleted)
2149 break;
2150 /* Make sure the loop is not too tight */
2151 rc = progress->WaitForCompletion(100);
2152 if (FAILED(rc)) throw rc;
2153 }
2154 // report result of asynchronous operation
2155 HRESULT vrc;
2156 rc = progress->COMGETTER(ResultCode)(&vrc);
2157 if (FAILED(rc)) throw rc;
2158
2159 // if the thread of the progress object has an error, then
2160 // retrieve the error info from there, or it'll be lost
2161 if (FAILED(vrc))
2162 {
2163 ProgressErrorInfo info(progress);
2164 Utf8Str str(info.getText());
2165 const char *pcsz = str.c_str();
2166 HRESULT rc2 = setError(vrc,
2167 pcsz);
2168 throw rc2;
2169 }
2170
2171 if (fSourceHdNeedsClosing)
2172 {
2173 rc = srcHdVBox->Close();
2174 if (FAILED(rc)) throw rc;
2175 fSourceHdNeedsClosing = false;
2176 }
2177
2178 llHardDisksCreated.push_back(dstHdVBox);
2179
2180 /* Now use the new uuid to attach the disk image to our new machine */
2181 ComPtr<IMachine> sMachine;
2182 rc = session->COMGETTER(Machine)(sMachine.asOutParam());
2183 if (FAILED(rc)) throw rc;
2184 Guid hdId;
2185 rc = dstHdVBox->COMGETTER(Id)(hdId.asOutParam());
2186 if (FAILED(rc)) throw rc;
2187
2188 /* For now we assume we have one controller of every type only */
2189 HardDiskController hdc = (*vsysThis.mapControllers.find(vd.idController)).second;
2190
2191 // this is for rollback later
2192 MyHardDiskAttachment mhda;
2193 mhda.uuid = newMachineId;
2194 mhda.pMachine = pNewMachine;
2195
2196 switch (hdc.system)
2197 {
2198 case HardDiskController::IDE:
2199 // For the IDE bus, the channel parameter can be either 0 or 1, to specify the primary
2200 // or secondary IDE controller, respectively. For the primary controller of the IDE bus,
2201 // the device number can be either 0 or 1, to specify the master or the slave device,
2202 // respectively. For the secondary IDE controller, the device number is always 1 because
2203 // the master device is reserved for the CD-ROM drive.
2204 mhda.controllerType = Bstr("IDE");
2205 switch (vd.ulAddressOnParent)
2206 {
2207 case 0: // interpret this as primary master
2208 mhda.lChannel = (long)0;
2209 mhda.lDevice = (long)0;
2210 break;
2211
2212 case 1: // interpret this as primary slave
2213 mhda.lChannel = (long)0;
2214 mhda.lDevice = (long)1;
2215 break;
2216
2217 case 2: // interpret this as secondary slave
2218 mhda.lChannel = (long)1;
2219 mhda.lDevice = (long)1;
2220 break;
2221
2222 default:
2223 throw setError(VBOX_E_NOT_SUPPORTED,
2224 tr("Invalid channel %RI16 specified; IDE controllers support only 0, 1 or 2"), vd.ulAddressOnParent);
2225 break;
2226 }
2227 break;
2228
2229 case HardDiskController::SATA:
2230 mhda.controllerType = Bstr("SATA");
2231 mhda.lChannel = (long)vd.ulAddressOnParent;
2232 mhda.lDevice = (long)0;
2233 break;
2234
2235 case HardDiskController::SCSI:
2236 mhda.controllerType = Bstr("SCSI");
2237 mhda.lChannel = (long)vd.ulAddressOnParent;
2238 mhda.lDevice = (long)0;
2239 break;
2240
2241 default: break;
2242 }
2243
2244 Log(("Attaching disk %s to channel %d on device %d\n", pcszDstFilePath, mhda.lChannel, mhda.lDevice));
2245
2246 rc = sMachine->AttachHardDisk(hdId,
2247 mhda.controllerType,
2248 mhda.lChannel,
2249 mhda.lDevice);
2250 if (FAILED(rc)) throw rc;
2251
2252 llHardDiskAttachments.push_back(mhda);
2253
2254 rc = sMachine->SaveSettings();
2255 if (FAILED(rc)) throw rc;
2256 } // end for (itHD = avsdeHDs.begin();
2257
2258 // only now that we're done with all disks, close the session
2259 rc = session->Close();
2260 if (FAILED(rc)) throw rc;
2261 fSessionOpen = false;
2262 }
2263 catch(HRESULT /* aRC */)
2264 {
2265 if (fSourceHdNeedsClosing)
2266 srcHdVBox->Close();
2267
2268 if (fSessionOpen)
2269 session->Close();
2270
2271 throw;
2272 }
2273 }
2274 }
2275 catch(HRESULT aRC)
2276 {
2277 rc = aRC;
2278 }
2279
2280 if (FAILED(rc))
2281 break;
2282
2283 } // for (it = pAppliance->m->llVirtualSystems.begin(),
2284
2285 if (FAILED(rc))
2286 {
2287 // with _whatever_ error we've had, do a complete roll-back of
2288 // machines and disks we've created; unfortunately this is
2289 // not so trivially done...
2290
2291 HRESULT rc2;
2292 // detach all hard disks from all machines we created
2293 list<MyHardDiskAttachment>::iterator itM;
2294 for (itM = llHardDiskAttachments.begin();
2295 itM != llHardDiskAttachments.end();
2296 ++itM)
2297 {
2298 const MyHardDiskAttachment &mhda = *itM;
2299 rc2 = pVirtualBox->OpenSession(session, mhda.uuid);
2300 if (SUCCEEDED(rc2))
2301 {
2302 ComPtr<IMachine> sMachine;
2303 rc2 = session->COMGETTER(Machine)(sMachine.asOutParam());
2304 if (SUCCEEDED(rc2))
2305 {
2306 rc2 = sMachine->DetachHardDisk(Bstr(mhda.controllerType), mhda.lChannel, mhda.lDevice);
2307 rc2 = sMachine->SaveSettings();
2308 }
2309 session->Close();
2310 }
2311 }
2312
2313 // now clean up all hard disks we created
2314 list< ComPtr<IHardDisk> >::iterator itHD;
2315 for (itHD = llHardDisksCreated.begin();
2316 itHD != llHardDisksCreated.end();
2317 ++itHD)
2318 {
2319 ComPtr<IHardDisk> pDisk = *itHD;
2320 ComPtr<IProgress> pProgress;
2321 rc2 = pDisk->DeleteStorage(pProgress.asOutParam());
2322 rc2 = pProgress->WaitForCompletion(-1);
2323 }
2324
2325 // finally, deregister and remove all machines
2326 list<Guid>::iterator itID;
2327 for (itID = llMachinesRegistered.begin();
2328 itID != llMachinesRegistered.end();
2329 ++itID)
2330 {
2331 const Guid &guid = *itID;
2332 ComPtr<IMachine> failedMachine;
2333 rc2 = pVirtualBox->UnregisterMachine(guid, failedMachine.asOutParam());
2334 if (SUCCEEDED(rc2))
2335 rc2 = failedMachine->DeleteSettings();
2336 }
2337 }
2338
2339 task->rc = rc;
2340
2341 if (!task->progress.isNull())
2342 task->progress->notifyComplete(rc);
2343
2344 LogFlowFunc(("rc=%Rhrc\n", rc));
2345 LogFlowFuncLeave();
2346
2347 return VINF_SUCCESS;
2348}
2349
2350/**
2351 * Worker thread implementation for Write() (ovf writer).
2352 * @param aThread
2353 * @param pvUser
2354 */
2355/* static */
2356DECLCALLBACK(int) Appliance::taskThreadWriteOVF(RTTHREAD aThread, void *pvUser)
2357{
2358 std::auto_ptr<TaskWriteOVF> task(static_cast<TaskWriteOVF*>(pvUser));
2359 AssertReturn(task.get(), VERR_GENERAL_FAILURE);
2360
2361 Appliance *pAppliance = task->pAppliance;
2362
2363 LogFlowFuncEnter();
2364 LogFlowFunc(("Appliance %p\n", pAppliance));
2365
2366 AutoCaller autoCaller(pAppliance);
2367 CheckComRCReturnRC(autoCaller.rc());
2368
2369 AutoWriteLock appLock(pAppliance);
2370
2371 HRESULT rc = S_OK;
2372
2373 ComPtr<IVirtualBox> pVirtualBox(pAppliance->mVirtualBox);
2374
2375 try
2376 {
2377 xml::Document doc;
2378 xml::ElementNode *pelmRoot = doc.createRootElement("Envelope");
2379
2380 pelmRoot->setAttribute("ovf:version", "1.0");
2381 pelmRoot->setAttribute("xml:lang", "en-US");
2382 pelmRoot->setAttribute("xmlns", "http://schemas.dmtf.org/ovf/envelope/1");
2383 pelmRoot->setAttribute("xmlns:ovf", "http://schemas.dmtf.org/ovf/envelope/1");
2384 pelmRoot->setAttribute("xmlns:ovfstr", "http://schema.dmtf.org/ovf/strings/1");
2385 pelmRoot->setAttribute("xmlns:rasd", "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData");
2386 pelmRoot->setAttribute("xmlns:vssd", "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData");
2387 pelmRoot->setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
2388 pelmRoot->setAttribute("xsi:schemaLocation", "http://schemas.dmtf.org/ovf/envelope/1 ../ovf-envelope.xsd");
2389
2390
2391 // <Envelope>/<References>
2392 xml::ElementNode *pelmReferences = pelmRoot->createChild("References");
2393 // @ŧodo
2394
2395 /* <Envelope>/<DiskSection>:
2396 <DiskSection>
2397 <Info>List of the virtual disks used in the package</Info>
2398 <Disk ovf:capacity="4294967296" ovf:diskId="lamp" ovf:format="http://www.vmware.com/specifications/vmdk.html#compressed" ovf:populatedSize="1924967692"/>
2399 </DiskSection> */
2400 xml::ElementNode *pelmDiskSection = pelmRoot->createChild("DiskSection");
2401 xml::ElementNode *pelmDiskSectionInfo = pelmDiskSection->createChild("Info");
2402 pelmDiskSectionInfo->addContent("List of the virtual disks used in the package");
2403 // for now, set up a map so we have a list of unique disk names (to make
2404 // sure the same disk name is only added once)
2405 map<Utf8Str, const VirtualSystemDescriptionEntry*> mapDisks;
2406
2407 /* <Envelope>/<NetworkSection>:
2408 <NetworkSection>
2409 <Info>Logical networks used in the package</Info>
2410 <Network ovf:name="VM Network">
2411 <Description>The network that the LAMP Service will be available on</Description>
2412 </Network>
2413 </NetworkSection> */
2414 xml::ElementNode *pelmNetworkSection = pelmRoot->createChild("NetworkSection");
2415 xml::ElementNode *pelmNetworkSectionInfo = pelmNetworkSection->createChild("Info");
2416 pelmNetworkSectionInfo->addContent("Logical networks used in the package");
2417 // for now, set up a map so we have a list of unique network names (to make
2418 // sure the same network name is only added once)
2419 map<Utf8Str, bool> mapNetworks;
2420 // we fill this later below when we iterate over the networks
2421
2422 // and here come the virtual systems:
2423 xml::ElementNode *pelmVirtualSystemCollection = pelmRoot->createChild("VirtualSystemCollection");
2424 xml::AttributeNode *pattrVirtualSystemCollectionId = pelmVirtualSystemCollection->setAttribute("ovf:id", "ExportedVirtualBoxMachines"); // whatever
2425
2426 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;
2427 /* Iterate through all virtual systems of that appliance */
2428 for (it = pAppliance->m->virtualSystemDescriptions.begin();
2429 it != pAppliance->m->virtualSystemDescriptions.end();
2430 ++it)
2431 {
2432 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);
2433
2434 xml::ElementNode *pelmVirtualSystem = pelmVirtualSystemCollection->createChild("VirtualSystem");
2435 xml::ElementNode *pelmVirtualSystemInfo = pelmVirtualSystem->createChild("Info"); // @todo put in description here after implementing an entry for it
2436
2437 std::list<VirtualSystemDescriptionEntry*> llName = vsdescThis->findByType(VirtualSystemDescriptionType_Name);
2438 if (llName.size() != 1)
2439 throw setError(VBOX_E_NOT_SUPPORTED,
2440 tr("Missing VM name"));
2441 pelmVirtualSystem->setAttribute("ovf:id", llName.front()->strVbox);
2442
2443 std::list<VirtualSystemDescriptionEntry*> llOS = vsdescThis->findByType(VirtualSystemDescriptionType_OS);
2444 if (llOS.size() != 1)
2445 throw setError(VBOX_E_NOT_SUPPORTED,
2446 tr("Missing OS type"));
2447 /* <OperatingSystemSection ovf:id="82">
2448 <Info>Guest Operating System</Info>
2449 <Description>Linux 2.6.x</Description>
2450 </OperatingSystemSection> */
2451 xml::ElementNode *pelmOperatingSystemSection = pelmVirtualSystem->createChild("OperatingSystemSection");
2452 pelmOperatingSystemSection->setAttribute("ovf:id", llOS.front()->strOvf);
2453 pelmOperatingSystemSection->createChild("Info")->addContent("blah"); // @ŧodo
2454 pelmOperatingSystemSection->createChild("Description")->addContent("blah"); // @ŧodo
2455
2456 // <VirtualHardwareSection ovf:id="hw1" ovf:transport="iso">
2457 xml::ElementNode *pelmVirtualHardwareSection = pelmVirtualSystem->createChild("VirtualHardwareSection");
2458
2459 /* <System>
2460 <vssd:Description>Description of the virtual hardware section.</vssd:Description>
2461 <vssd:ElementName>vmware</vssd:ElementName>
2462 <vssd:InstanceID>1</vssd:InstanceID>
2463 <vssd:VirtualSystemIdentifier>MyLampService</vssd:VirtualSystemIdentifier>
2464 <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType>
2465 </System> */
2466 xml::ElementNode *pelmSystem = pelmVirtualHardwareSection->createChild("System");
2467
2468 // <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType>
2469 xml::ElementNode *pelmVirtualSystemType = pelmSystem->createChild("VirtualSystemType");
2470 pelmVirtualSystemType->addContent("virtualbox-2.2"); // instead of vmx-7?
2471
2472 // loop thru all description entries twice; once to write out all
2473 // devices _except_ disk images, and a second time to assign the
2474 // disk images; this is because disk images need to reference
2475 // IDE controllers, and we can't know their instance IDs without
2476 // assigning them first
2477
2478 uint32_t idIDEController = 0;
2479 int32_t lIDEControllerIndex = 0;
2480 uint32_t idSATAController = 0;
2481 int32_t lSATAControllerIndex = 0;
2482 uint32_t idSCSIController = 0;
2483 int32_t lSCSIControllerIndex = 0;
2484
2485 uint32_t ulInstanceID = 1;
2486 uint32_t cDisks = 0;
2487
2488 for (size_t uLoop = 1;
2489 uLoop <= 2;
2490 ++uLoop)
2491 {
2492 int32_t lIndexThis = 0;
2493 list<VirtualSystemDescriptionEntry>::const_iterator itD;
2494 for (itD = vsdescThis->m->llDescriptions.begin();
2495 itD != vsdescThis->m->llDescriptions.end();
2496 ++itD, ++lIndexThis)
2497 {
2498 const VirtualSystemDescriptionEntry &desc = *itD;
2499
2500 OVFResourceType_T type = (OVFResourceType_T)0; // if this becomes != 0 then we do stuff
2501 Utf8Str strResourceSubType;
2502
2503 Utf8Str strDescription; // results in <rasd:Description>...</rasd:Description> block
2504 Utf8Str strCaption; // results in <rasd:Caption>...</rasd:Caption> block
2505
2506 uint32_t ulParent = 0;
2507
2508 int32_t lVirtualQuantity = -1;
2509 Utf8Str strAllocationUnits;
2510
2511 int32_t lAddress = -1;
2512 int32_t lBusNumber = -1;
2513 int32_t lAddressOnParent = -1;
2514
2515 int32_t lAutomaticAllocation = -1; // 0 means "false", 1 means "true"
2516 Utf8Str strConnection; // results in <rasd:Connection>...</rasd:Connection> block
2517 Utf8Str strHostResource;
2518
2519 uint64_t uTemp;
2520
2521 switch (desc.type)
2522 {
2523 case VirtualSystemDescriptionType_CPU:
2524 /* <Item>
2525 <rasd:Caption>1 virtual CPU</rasd:Caption>
2526 <rasd:Description>Number of virtual CPUs</rasd:Description>
2527 <rasd:ElementName>virtual CPU</rasd:ElementName>
2528 <rasd:InstanceID>1</rasd:InstanceID>
2529 <rasd:ResourceType>3</rasd:ResourceType>
2530 <rasd:VirtualQuantity>1</rasd:VirtualQuantity>
2531 </Item> */
2532 if (uLoop == 1)
2533 {
2534 strDescription = "Number of virtual CPUs";
2535 type = OVFResourceType_Processor; // 3
2536 lVirtualQuantity = 1;
2537 }
2538 break;
2539
2540 case VirtualSystemDescriptionType_Memory:
2541 /* <Item>
2542 <rasd:AllocationUnits>MegaBytes</rasd:AllocationUnits>
2543 <rasd:Caption>256 MB of memory</rasd:Caption>
2544 <rasd:Description>Memory Size</rasd:Description>
2545 <rasd:ElementName>Memory</rasd:ElementName>
2546 <rasd:InstanceID>2</rasd:InstanceID>
2547 <rasd:ResourceType>4</rasd:ResourceType>
2548 <rasd:VirtualQuantity>256</rasd:VirtualQuantity>
2549 </Item> */
2550 if (uLoop == 1)
2551 {
2552 strDescription = "Memory Size";
2553 type = OVFResourceType_Memory; // 4
2554 desc.strVbox.toInt(uTemp);
2555 lVirtualQuantity = (int32_t)(uTemp / _1M);
2556 strAllocationUnits = "MegaBytes";
2557 }
2558 break;
2559
2560 case VirtualSystemDescriptionType_HardDiskControllerIDE:
2561 /* <Item>
2562 <rasd:Caption>ideController1</rasd:Caption>
2563 <rasd:Description>IDE Controller</rasd:Description>
2564 <rasd:InstanceId>5</rasd:InstanceId>
2565 <rasd:ResourceType>5</rasd:ResourceType>
2566 <rasd:Address>1</rasd:Address>
2567 <rasd:BusNumber>1</rasd:BusNumber>
2568 </Item> */
2569 if (uLoop == 1)
2570 {
2571 strDescription = "IDE Controller";
2572 type = OVFResourceType_IdeController; // 5
2573 // it seems that OVFTool always writes these two, and since we can only
2574 // have one IDE controller, we'll use this as well
2575 lAddress = 1;
2576 lBusNumber = 1;
2577
2578 // remember this ID
2579 idIDEController = ulInstanceID;
2580 lIDEControllerIndex = lIndexThis;
2581 }
2582 break;
2583
2584// case VirtualSystemDescriptionType_HardDiskControllerSATA: // @todo
2585// break;
2586
2587 case VirtualSystemDescriptionType_HardDiskControllerSCSI:
2588 /* <Item>
2589 <rasd:Caption>scsiController0</rasd:Caption>
2590 <rasd:Description>SCSI Controller</rasd:Description>
2591 <rasd:InstanceId>4</rasd:InstanceId>
2592 <rasd:ResourceType>6</rasd:ResourceType>
2593 <rasd:ResourceSubType>buslogic</rasd:ResourceSubType>
2594 <rasd:Address>0</rasd:Address>
2595 <rasd:BusNumber>0</rasd:BusNumber>
2596 </Item>
2597 */
2598 if (uLoop == 1)
2599 {
2600 strDescription = "SCSI Controller";
2601 strCaption = "scsiController0";
2602 type = OVFResourceType_ParallelScsiHba; // 6
2603 // it seems that OVFTool always writes these two, and since we can only
2604 // have one SATA controller, we'll use this as well
2605 lAddress = 0;
2606 lBusNumber = 0;
2607
2608 if (!desc.strVbox.compare("buslogic"), Utf8Str::CaseInsensitive)
2609 strResourceSubType = "buslogic";
2610 else if (!desc.strVbox.compare("lsilogic"), Utf8Str::CaseInsensitive)
2611 strResourceSubType = "lsilogic";
2612 else
2613 throw setError(VBOX_E_NOT_SUPPORTED,
2614 tr("Invalid config string \"%s\" in SCSI controller"), desc.strVbox.c_str());
2615
2616 // remember this ID
2617 idSCSIController = ulInstanceID;
2618 lSCSIControllerIndex = lIndexThis;
2619 }
2620 break;
2621
2622 case VirtualSystemDescriptionType_HardDiskImage:
2623 /* <Item>
2624 <rasd:Caption>disk1</rasd:Caption>
2625 <rasd:InstanceId>8</rasd:InstanceId>
2626 <rasd:ResourceType>17</rasd:ResourceType>
2627 <rasd:HostResource>/disk/vmdisk1</rasd:HostResource>
2628 <rasd:Parent>4</rasd:Parent>
2629 <rasd:AddressOnParent>0</rasd:AddressOnParent>
2630 </Item> */
2631 if (uLoop == 2)
2632 {
2633 Utf8Str strDiskID = Utf8StrFmt("vmdisk%RI32", ++cDisks);
2634
2635 strDescription = "Disk Image";
2636 strCaption = Utf8StrFmt("disk%RI32", cDisks); // this is not used for anything else
2637 type = OVFResourceType_HardDisk; // 17
2638
2639 // the following references the "<Disks>" XML block
2640 strHostResource = Utf8StrFmt("/disk/%s", strDiskID.c_str());
2641
2642 // controller=<index>;channel=<c>
2643 size_t pos1 = desc.strExtraConfig.find("controller=");
2644 size_t pos2 = desc.strExtraConfig.find("channel=");
2645 if (pos1 != Utf8Str::npos)
2646 {
2647 int32_t lControllerIndex = -1;
2648 RTStrToInt32Ex(desc.strExtraConfig.c_str() + pos1 + 11, NULL, 0, &lControllerIndex);
2649 if (lControllerIndex == lIDEControllerIndex)
2650 ulParent = idIDEController;
2651 else if (lControllerIndex == lSCSIControllerIndex)
2652 ulParent = idSCSIController;
2653 else if (lControllerIndex == lSATAControllerIndex)
2654 ulParent = idSATAController;
2655 }
2656 if (pos2 != Utf8Str::npos)
2657 RTStrToInt32Ex(desc.strExtraConfig.c_str() + pos2 + 8, NULL, 0, &lAddressOnParent);
2658
2659 if ( !ulParent
2660 || lAddressOnParent == -1
2661 )
2662 throw setError(VBOX_E_NOT_SUPPORTED,
2663 tr("Missing or bad extra config string in hard disk image: \"%s\""), desc.strExtraConfig.c_str());
2664
2665 mapDisks[strDiskID] = &desc;
2666 }
2667 break;
2668
2669 case VirtualSystemDescriptionType_Floppy:
2670 if (uLoop == 1)
2671 {
2672 strDescription = "Floppy Drive";
2673 strCaption = "floppy0"; // this is what OVFTool writes
2674 type = OVFResourceType_FloppyDrive; // 14
2675 lAutomaticAllocation = 0;
2676 lAddressOnParent = 0; // this is what OVFTool writes
2677 }
2678 break;
2679
2680 case VirtualSystemDescriptionType_CDROM:
2681 if (uLoop == 2)
2682 {
2683 // we can't have a CD without an IDE controller
2684 if (!idIDEController)
2685 throw setError(VBOX_E_NOT_SUPPORTED,
2686 tr("Can't have CD-ROM without IDE controller"));
2687
2688 strDescription = "CD-ROM Drive";
2689 strCaption = "cdrom1"; // this is what OVFTool writes
2690 type = OVFResourceType_CdDrive; // 15
2691 lAutomaticAllocation = 1;
2692 ulParent = idIDEController;
2693 lAddressOnParent = 0; // this is what OVFTool writes
2694 }
2695 break;
2696
2697 case VirtualSystemDescriptionType_NetworkAdapter:
2698 /* <Item>
2699 <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
2700 <rasd:Caption>Ethernet adapter on 'VM Network'</rasd:Caption>
2701 <rasd:Connection>VM Network</rasd:Connection>
2702 <rasd:ElementName>VM network</rasd:ElementName>
2703 <rasd:InstanceID>3</rasd:InstanceID>
2704 <rasd:ResourceType>10</rasd:ResourceType>
2705 </Item> */
2706 if (uLoop == 1)
2707 {
2708 lAutomaticAllocation = 1;
2709 strCaption = Utf8StrFmt("Ethernet adapter on '%s'", desc.strOvf.c_str());
2710 type = OVFResourceType_EthernetAdapter; // 10
2711 strConnection = desc.strOvf;
2712
2713 mapNetworks[desc.strOvf] = true;
2714 }
2715 break;
2716
2717 case VirtualSystemDescriptionType_USBController:
2718 /* <Item ovf:required="false">
2719 <rasd:Caption>usb</rasd:Caption>
2720 <rasd:Description>USB Controller</rasd:Description>
2721 <rasd:InstanceId>3</rasd:InstanceId>
2722 <rasd:ResourceType>23</rasd:ResourceType>
2723 <rasd:Address>0</rasd:Address>
2724 <rasd:BusNumber>0</rasd:BusNumber>
2725 </Item> */
2726 if (uLoop == 1)
2727 {
2728 strDescription = "USB Controller";
2729 strCaption = "usb";
2730 type = OVFResourceType_UsbController; // 23
2731 lAddress = 0; // this is what OVFTool writes
2732 lBusNumber = 0; // this is what OVFTool writes
2733 }
2734 break;
2735
2736 case VirtualSystemDescriptionType_SoundCard:
2737 /* <Item ovf:required="false">
2738 <rasd:Caption>sound</rasd:Caption>
2739 <rasd:Description>Sound Card</rasd:Description>
2740 <rasd:InstanceId>10</rasd:InstanceId>
2741 <rasd:ResourceType>35</rasd:ResourceType>
2742 <rasd:ResourceSubType>ensoniq1371</rasd:ResourceSubType>
2743 <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
2744 <rasd:AddressOnParent>3</rasd:AddressOnParent>
2745 </Item> */
2746 if (uLoop == 1)
2747 {
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 }
2755 break;
2756 }
2757
2758 if (type)
2759 {
2760 xml::ElementNode *pItem;
2761
2762 pItem = pelmVirtualHardwareSection->createChild("Item");
2763
2764 if (!strDescription.isEmpty())
2765 pItem->createChild("rasd:Description")->addContent(strDescription);
2766 if (!strCaption.isEmpty())
2767 pItem->createChild("rasd:Caption")->addContent(strCaption);
2768
2769 if (!strAllocationUnits.isEmpty())
2770 pItem->createChild("rasd:AllocationUnits")->addContent(strAllocationUnits);
2771
2772 if (lAutomaticAllocation != -1)
2773 pItem->createChild("rasd:AutomaticAllocation")->addContent( (lAutomaticAllocation) ? "true" : "false" );
2774
2775 if (!strConnection.isEmpty())
2776 pItem->createChild("rasd:Connection")->addContent(strConnection);
2777
2778 // <rasd:InstanceID>1</rasd:InstanceID>
2779 pItem->createChild("rasd:InstanceID")->addContent(Utf8StrFmt("%d", ulInstanceID));
2780 ++ulInstanceID;
2781
2782 // <rasd:ResourceType>3</rasd:ResourceType>
2783 pItem->createChild("rasd:ResourceType")->addContent(Utf8StrFmt("%d", type));
2784 if (!strResourceSubType.isEmpty())
2785 pItem->createChild("rasd:ResourceSubType")->addContent(strResourceSubType);
2786
2787 // <rasd:VirtualQuantity>1</rasd:VirtualQuantity>
2788 if (lVirtualQuantity != -1)
2789 pItem->createChild("rasd:VirtualQuantity")->addContent(Utf8StrFmt("%d", lVirtualQuantity));
2790
2791 if (lAddress != -1)
2792 pItem->createChild("rasd:Address")->addContent(Utf8StrFmt("%d", lAddress));
2793
2794 if (lBusNumber != -1)
2795 pItem->createChild("rasd:BusNumber")->addContent(Utf8StrFmt("%d", lBusNumber));
2796
2797 if (ulParent)
2798 pItem->createChild("rasd:Parent")->addContent(Utf8StrFmt("%d", ulParent));
2799 if (lAddressOnParent != -1)
2800 pItem->createChild("rasd:AddressOnParent")->addContent(Utf8StrFmt("%d", lAddressOnParent));
2801
2802 if (!strHostResource.isEmpty())
2803 pItem->createChild("rasd:HostResource")->addContent(strHostResource);
2804 }
2805 }
2806 } // for (size_t uLoop = 0; ...
2807 }
2808
2809 // finally, fill in the network section we set up empty above according
2810 // to the networks we found with the hardware items
2811 map<Utf8Str, bool>::const_iterator itN;
2812 for (itN = mapNetworks.begin();
2813 itN != mapNetworks.end();
2814 ++itN)
2815 {
2816 const Utf8Str &strNetwork = itN->first;
2817 xml::ElementNode *pelmNetwork = pelmNetworkSection->createChild("Network");
2818 pelmNetwork->setAttribute("ovf:name", strNetwork.c_str());
2819 pelmNetwork->createChild("Description")->addContent("Logical network used by this appliance.");
2820 }
2821
2822 map<Utf8Str, const VirtualSystemDescriptionEntry*>::const_iterator itS;
2823 uint32_t ulFile = 1;
2824 for (itS = mapDisks.begin();
2825 itS != mapDisks.end();
2826 ++itS)
2827 {
2828 const Utf8Str &strDiskID = itS->first;
2829 const VirtualSystemDescriptionEntry *pDiskEntry = itS->second;
2830
2831 // source path: where the VBox image is
2832 const Utf8Str &strSrcFilePath = pDiskEntry->strVbox;
2833 Bstr bstrSrcFilePath(strSrcFilePath);
2834 if (!RTPathExists(strSrcFilePath.c_str()))
2835 /* This isn't allowed */
2836 throw setError(VBOX_E_FILE_ERROR,
2837 tr("Source virtual disk image file '%s' doesn't exist"),
2838 strSrcFilePath.c_str());
2839
2840 // output filename
2841 const Utf8Str &strTargetFileNameOnly = pDiskEntry->strOvf;
2842 // target path needs to be composed from where the output OVF is
2843 Utf8Str strTargetFilePath = stripFilename(pAppliance->m->strPath);
2844 strTargetFilePath.append("/");
2845 strTargetFilePath.append(strTargetFileNameOnly);
2846
2847 // clone the disk:
2848 ComPtr<IHardDisk> pSourceDisk;
2849 ComPtr<IHardDisk> pTargetDisk;
2850 ComPtr<IProgress> pProgress2;
2851
2852 Log(("Finding source disk \"%ls\"\n", bstrSrcFilePath.raw()));
2853 rc = pVirtualBox->FindHardDisk(bstrSrcFilePath, pSourceDisk.asOutParam());
2854 if (FAILED(rc)) throw rc;
2855
2856 /* Based on the file extensions we choose the right format for the
2857 * disk */
2858 Bstr bstrSrcFormat = L"VDI";
2859 if (strTargetFilePath.endsWith(".vmdk", Utf8Str::CaseInsensitive))
2860 bstrSrcFormat = L"VMDK";
2861 /* Create a new hard disk interface for the destination disk image */
2862 Log(("Creating target disk \"%s\"\n", strTargetFilePath.raw()));
2863 rc = pVirtualBox->CreateHardDisk(bstrSrcFormat, Bstr(strTargetFilePath), pTargetDisk.asOutParam());
2864 if (FAILED(rc)) throw rc;
2865 // clone the source disk image
2866 rc = pSourceDisk->CloneTo(pTargetDisk, pProgress2.asOutParam());
2867 if (FAILED(rc)) throw rc;
2868
2869 // advance to the next operation
2870 if (!task->progress.isNull())
2871 task->progress->advanceOperation(BstrFmt(tr("Exporting virtual disk image '%s'"), strSrcFilePath.c_str()));
2872
2873 // now loop until the asynchronous operation completes and then
2874 // report its result
2875 BOOL fCompleted;
2876 LONG currentPercent;
2877 while (SUCCEEDED(pProgress2->COMGETTER(Completed(&fCompleted))))
2878 {
2879 rc = pProgress2->COMGETTER(Percent(&currentPercent));
2880 if (FAILED(rc)) throw rc;
2881 if (!task->progress.isNull())
2882 task->progress->notifyProgress(currentPercent);
2883 if (fCompleted)
2884 break;
2885 // make sure the loop is not too tight
2886 rc = pProgress2->WaitForCompletion(100);
2887 if (FAILED(rc)) throw rc;
2888 }
2889 // report result of asynchronous operation
2890 HRESULT vrc;
2891 rc = pProgress2->COMGETTER(ResultCode)(&vrc);
2892 if (FAILED(rc)) throw rc;
2893
2894 // if the thread of the progress object has an error, then
2895 // retrieve the error info from there, or it'll be lost
2896 if (FAILED(vrc))
2897 {
2898 ProgressErrorInfo info(pProgress2);
2899 Utf8Str str(info.getText());
2900 const char *pcsz = str.c_str();
2901 HRESULT rc2 = setError(vrc, pcsz);
2902 throw rc2;
2903 }
2904
2905 /* Make sure the target disk get detached */
2906 rc = pTargetDisk->Close(); // @todo close this also if an error is thrown above
2907 if (FAILED(rc)) throw rc;
2908
2909 // we need the capacity and actual file size for the XML
2910 uint64_t cbFile = 12345678; // @todo
2911 uint64_t cbCapacity = 2345678; // @todo
2912
2913 // now handle the XML for the disk:
2914 Utf8StrFmt strFileRef("file%RI32", ulFile++);
2915 // <File ovf:href="WindowsXpProfessional-disk1.vmdk" ovf:id="file1" ovf:size="1710381056"/>
2916 xml::ElementNode *pelmFile = pelmReferences->createChild("File");
2917 pelmFile->setAttribute("ovf:href", strTargetFileNameOnly);
2918 pelmFile->setAttribute("ovf:id", strFileRef);
2919 pelmFile->setAttribute("ovf:size", Utf8StrFmt("%RI64", cbFile).c_str()); // @todo
2920
2921 // add disk to XML Disks section
2922 // <Disk ovf:capacity="8589934592" ovf:diskId="vmdisk1" ovf:fileRef="file1" ovf:format="http://www.vmware.com/specifications/vmdk.html#sparse"/>
2923 xml::ElementNode *pelmDisk = pelmDiskSection->createChild("Disk");
2924 pelmDisk->setAttribute("ovf:capacity", Utf8StrFmt("%RI64", cbCapacity).c_str()); // @todo
2925 pelmDisk->setAttribute("ovf:diskId", strDiskID);
2926 pelmDisk->setAttribute("ovf:fileRef", strFileRef);
2927 pelmDisk->setAttribute("ovf:format", "http://www.vmware.com/specifications/vmdk.html#sparse");
2928 }
2929
2930 // now go write the XML
2931 xml::XmlFileWriter writer(doc);
2932 writer.write(pAppliance->m->strPath.c_str());
2933 }
2934 catch(xml::Error &x)
2935 {
2936 rc = setError(VBOX_E_FILE_ERROR,
2937 x.what());
2938 }
2939 catch(HRESULT aRC)
2940 {
2941 rc = aRC;
2942 }
2943
2944 task->rc = rc;
2945
2946 if (!task->progress.isNull())
2947 task->progress->notifyComplete(rc);
2948
2949 LogFlowFunc(("rc=%Rhrc\n", rc));
2950 LogFlowFuncLeave();
2951
2952 return VINF_SUCCESS;
2953}
2954
2955////////////////////////////////////////////////////////////////////////////////
2956//
2957// IVirtualSystemDescription constructor / destructor
2958//
2959////////////////////////////////////////////////////////////////////////////////
2960
2961DEFINE_EMPTY_CTOR_DTOR(VirtualSystemDescription)
2962struct shutup3 {};
2963
2964/**
2965 * COM initializer.
2966 * @return
2967 */
2968HRESULT VirtualSystemDescription::init()
2969{
2970 /* Enclose the state transition NotReady->InInit->Ready */
2971 AutoInitSpan autoInitSpan(this);
2972 AssertReturn(autoInitSpan.isOk(), E_FAIL);
2973
2974 /* Initialize data */
2975 m = new Data();
2976
2977 /* Confirm a successful initialization */
2978 autoInitSpan.setSucceeded();
2979 return S_OK;
2980}
2981
2982/**
2983* COM uninitializer.
2984*/
2985
2986void VirtualSystemDescription::uninit()
2987{
2988 delete m;
2989 m = NULL;
2990}
2991
2992////////////////////////////////////////////////////////////////////////////////
2993//
2994// IVirtualSystemDescription public methods
2995//
2996////////////////////////////////////////////////////////////////////////////////
2997
2998/**
2999 * Public method implementation.
3000 * @param
3001 * @return
3002 */
3003STDMETHODIMP VirtualSystemDescription::COMGETTER(Count)(ULONG *aCount)
3004{
3005 if (!aCount)
3006 return E_POINTER;
3007
3008 AutoCaller autoCaller(this);
3009 CheckComRCReturnRC(autoCaller.rc());
3010
3011 AutoReadLock alock(this);
3012
3013 *aCount = (ULONG)m->llDescriptions.size();
3014
3015 return S_OK;
3016}
3017
3018/**
3019 * Public method implementation.
3020 * @return
3021 */
3022STDMETHODIMP VirtualSystemDescription::GetDescription(ComSafeArrayOut(VirtualSystemDescriptionType_T, aTypes),
3023 ComSafeArrayOut(BSTR, aRefs),
3024 ComSafeArrayOut(BSTR, aOrigValues),
3025 ComSafeArrayOut(BSTR, aVboxValues),
3026 ComSafeArrayOut(BSTR, aExtraConfigValues))
3027{
3028 if (ComSafeArrayOutIsNull(aTypes) ||
3029 ComSafeArrayOutIsNull(aRefs) ||
3030 ComSafeArrayOutIsNull(aOrigValues) ||
3031 ComSafeArrayOutIsNull(aVboxValues) ||
3032 ComSafeArrayOutIsNull(aExtraConfigValues))
3033 return E_POINTER;
3034
3035 AutoCaller autoCaller(this);
3036 CheckComRCReturnRC(autoCaller.rc());
3037
3038 AutoReadLock alock(this);
3039
3040 ULONG c = (ULONG)m->llDescriptions.size();
3041 com::SafeArray<VirtualSystemDescriptionType_T> sfaTypes(c);
3042 com::SafeArray<BSTR> sfaRefs(c);
3043 com::SafeArray<BSTR> sfaOrigValues(c);
3044 com::SafeArray<BSTR> sfaVboxValues(c);
3045 com::SafeArray<BSTR> sfaExtraConfigValues(c);
3046
3047 list<VirtualSystemDescriptionEntry>::const_iterator it;
3048 size_t i = 0;
3049 for (it = m->llDescriptions.begin();
3050 it != m->llDescriptions.end();
3051 ++it, ++i)
3052 {
3053 const VirtualSystemDescriptionEntry &vsde = (*it);
3054
3055 sfaTypes[i] = vsde.type;
3056
3057 Bstr bstr = vsde.strRef;
3058 bstr.cloneTo(&sfaRefs[i]);
3059
3060 bstr = vsde.strOvf;
3061 bstr.cloneTo(&sfaOrigValues[i]);
3062
3063 bstr = vsde.strVbox;
3064 bstr.cloneTo(&sfaVboxValues[i]);
3065
3066 bstr = vsde.strExtraConfig;
3067 bstr.cloneTo(&sfaExtraConfigValues[i]);
3068 }
3069
3070 sfaTypes.detachTo(ComSafeArrayOutArg(aTypes));
3071 sfaRefs.detachTo(ComSafeArrayOutArg(aRefs));
3072 sfaOrigValues.detachTo(ComSafeArrayOutArg(aOrigValues));
3073 sfaVboxValues.detachTo(ComSafeArrayOutArg(aVboxValues));
3074 sfaExtraConfigValues.detachTo(ComSafeArrayOutArg(aExtraConfigValues));
3075
3076 return S_OK;
3077}
3078
3079/**
3080 * Public method implementation.
3081 * @return
3082 */
3083STDMETHODIMP VirtualSystemDescription::SetFinalValues(ComSafeArrayIn(BOOL, aEnabled),
3084 ComSafeArrayIn(IN_BSTR, argVboxValues),
3085 ComSafeArrayIn(IN_BSTR, argExtraConfigValues))
3086{
3087 CheckComArgSafeArrayNotNull(argVboxValues);
3088 CheckComArgSafeArrayNotNull(argExtraConfigValues);
3089
3090 AutoCaller autoCaller(this);
3091 CheckComRCReturnRC(autoCaller.rc());
3092
3093 AutoWriteLock alock(this);
3094
3095 com::SafeArray<IN_BSTR> aVboxValues(ComSafeArrayInArg(argVboxValues));
3096 com::SafeArray<IN_BSTR> aExtraConfigValues(ComSafeArrayInArg(argExtraConfigValues));
3097
3098 if ( (aVboxValues.size() != m->llDescriptions.size())
3099 || (aExtraConfigValues.size() != m->llDescriptions.size())
3100 )
3101 return E_INVALIDARG;
3102
3103 list<VirtualSystemDescriptionEntry>::iterator it;
3104 size_t i = 0;
3105 for (it = m->llDescriptions.begin();
3106 it != m->llDescriptions.end();
3107 ++it, ++i)
3108 {
3109 VirtualSystemDescriptionEntry& vsde = *it;
3110
3111 if (aEnabled[i])
3112 {
3113 vsde.strVbox = aVboxValues[i];
3114 vsde.strExtraConfig = aExtraConfigValues[i];
3115 }
3116 else
3117 vsde.type = VirtualSystemDescriptionType_Ignore;
3118 }
3119
3120 return S_OK;
3121}
3122
3123/**
3124* Public method implementation.
3125 * @return
3126 */
3127STDMETHODIMP VirtualSystemDescription::GetWarnings(ComSafeArrayOut(BSTR, aWarnings))
3128{
3129 if (ComSafeArrayOutIsNull(aWarnings))
3130 return E_POINTER;
3131
3132 AutoCaller autoCaller(this);
3133 CheckComRCReturnRC(autoCaller.rc());
3134
3135 AutoReadLock alock(this);
3136
3137 com::SafeArray<BSTR> sfaWarnings(m->llWarnings.size());
3138
3139 list<Utf8Str>::const_iterator it;
3140 size_t i = 0;
3141 for (it = m->llWarnings.begin();
3142 it != m->llWarnings.end();
3143 ++it, ++i)
3144 {
3145 Bstr bstr = *it;
3146 bstr.cloneTo(&sfaWarnings[i]);
3147 }
3148
3149 sfaWarnings.detachTo(ComSafeArrayOutArg(aWarnings));
3150
3151 return S_OK;
3152}
3153
3154/**
3155 * Internal method; adds a new description item to the member list.
3156 * @param aType Type of description for the new item.
3157 * @param strRef Reference item; only used with hard disk controllers.
3158 * @param aOrigValue Corresponding original value from OVF.
3159 * @param aAutoValue Initial configuration value (can be overridden by caller with setFinalValues).
3160 * @param strExtraConfig Extra configuration; meaning dependent on type.
3161 */
3162void VirtualSystemDescription::addEntry(VirtualSystemDescriptionType_T aType,
3163 const Utf8Str &strRef,
3164 const Utf8Str &aOrigValue,
3165 const Utf8Str &aAutoValue,
3166 const Utf8Str &strExtraConfig /*= ""*/)
3167{
3168 VirtualSystemDescriptionEntry vsde;
3169 vsde.ulIndex = (uint32_t)m->llDescriptions.size(); // each entry gets an index so the client side can reference them
3170 vsde.type = aType;
3171 vsde.strRef = strRef;
3172 vsde.strOvf = aOrigValue;
3173 vsde.strVbox = aAutoValue;
3174 vsde.strExtraConfig = strExtraConfig;
3175
3176 m->llDescriptions.push_back(vsde);
3177}
3178
3179void VirtualSystemDescription::addWarning(const char* aWarning, ...)
3180{
3181 va_list args;
3182 va_start(args, aWarning);
3183 Utf8StrFmtVA str(aWarning, args);
3184 va_end(args);
3185 m->llWarnings.push_back(str);
3186}
3187
3188/**
3189 * Private method; returns a list of description items containing all the items from the member
3190 * description items of this virtual system that match the given type.
3191 * @param aType
3192 * @return
3193 */
3194std::list<VirtualSystemDescriptionEntry*> VirtualSystemDescription::findByType(VirtualSystemDescriptionType_T aType)
3195{
3196 std::list<VirtualSystemDescriptionEntry*> vsd;
3197
3198 list<VirtualSystemDescriptionEntry>::iterator it;
3199 for (it = m->llDescriptions.begin();
3200 it != m->llDescriptions.end();
3201 ++it)
3202 {
3203 if (it->type == aType)
3204 vsd.push_back(&(*it));
3205 }
3206
3207 return vsd;
3208}
3209
3210/**
3211 * Private method; looks thru the member hardware items for the IDE, SATA, or SCSI controller with
3212 * the given reference ID. Useful when needing the controller for a particular
3213 * virtual disk.
3214 * @param id
3215 * @return
3216 */
3217const VirtualSystemDescriptionEntry* VirtualSystemDescription::findControllerFromID(uint32_t id)
3218{
3219 Utf8Str strRef = Utf8StrFmt("%RI32", id);
3220 list<VirtualSystemDescriptionEntry>::const_iterator it;
3221 for (it = m->llDescriptions.begin();
3222 it != m->llDescriptions.end();
3223 ++it)
3224 {
3225 switch (it->type)
3226 {
3227 case VirtualSystemDescriptionType_HardDiskControllerIDE:
3228 case VirtualSystemDescriptionType_HardDiskControllerSATA:
3229 case VirtualSystemDescriptionType_HardDiskControllerSCSI:
3230 if (it->strRef == strRef)
3231 return &(*it);
3232 break;
3233 }
3234 }
3235
3236 return NULL;
3237}
3238
3239////////////////////////////////////////////////////////////////////////////////
3240//
3241// IMachine public methods
3242//
3243////////////////////////////////////////////////////////////////////////////////
3244
3245// This code is here so we won't have to include the appliance headers in the
3246// IMachine implementation, and we also need to access private appliance data.
3247
3248/**
3249* Public method implementation.
3250* @param appliance
3251* @return
3252*/
3253
3254STDMETHODIMP Machine::Export(IAppliance *appliance)
3255{
3256 HRESULT rc = S_OK;
3257
3258 if (!appliance)
3259 return E_POINTER;
3260
3261 AutoCaller autoCaller(this);
3262 CheckComRCReturnRC(autoCaller.rc());
3263
3264 AutoReadLock alock(this);
3265
3266 ComObjPtr<VirtualSystemDescription> pNewDesc;
3267
3268 try
3269 {
3270 Bstr bstrName;
3271 Bstr bstrDescription;
3272 Bstr bstrGuestOSType;
3273 uint32_t cCPUs;
3274 uint32_t ulMemSizeMB;
3275 BOOL fDVDEnabled;
3276 BOOL fFloppyEnabled;
3277 BOOL fUSBEnabled;
3278 BOOL fAudioEnabled;
3279 AudioControllerType_T audioController;
3280
3281 ComPtr<IUSBController> pUsbController;
3282 ComPtr<IAudioAdapter> pAudioAdapter;
3283
3284 // get name
3285 bstrName = mUserData->mName;
3286 // get description
3287 bstrDescription = mUserData->mDescription;
3288 // get guest OS
3289 bstrGuestOSType = mUserData->mOSTypeId;
3290 // CPU count
3291 cCPUs = mHWData->mCPUCount;
3292 // memory size in MB
3293 ulMemSizeMB = mHWData->mMemorySize;
3294 // VRAM size?
3295 // BIOS settings?
3296 // 3D acceleration enabled?
3297 // hardware virtualization enabled?
3298 // nested paging enabled?
3299 // HWVirtExVPIDEnabled?
3300 // PAEEnabled?
3301 // snapshotFolder?
3302 // VRDPServer?
3303
3304 // floppy
3305 rc = mFloppyDrive->COMGETTER(Enabled)(&fFloppyEnabled);
3306 if (FAILED(rc)) throw rc;
3307
3308 // CD-ROM ?!?
3309 // ComPtr<IDVDDrive> pDVDDrive;
3310 fDVDEnabled = 1;
3311
3312 // this is more tricky so use the COM method
3313 rc = COMGETTER(USBController)(pUsbController.asOutParam());
3314 if (FAILED(rc)) throw rc;
3315 rc = pUsbController->COMGETTER(Enabled)(&fUSBEnabled);
3316
3317 pAudioAdapter = mAudioAdapter;
3318 rc = pAudioAdapter->COMGETTER(Enabled)(&fAudioEnabled);
3319 if (FAILED(rc)) throw rc;
3320 rc = pAudioAdapter->COMGETTER(AudioController)(&audioController);
3321 if (FAILED(rc)) throw rc;
3322
3323 // create a new virtual system
3324 rc = pNewDesc.createObject();
3325 CheckComRCThrowRC(rc);
3326 rc = pNewDesc->init();
3327 CheckComRCThrowRC(rc);
3328
3329 /* Guest OS type */
3330 Utf8Str strOsTypeVBox(bstrGuestOSType);
3331 CIMOSType_T cim = convertVBoxOSType2CIMOSType(strOsTypeVBox.c_str());
3332 pNewDesc->addEntry(VirtualSystemDescriptionType_OS,
3333 "",
3334 Utf8StrFmt("%RI32", cim),
3335 strOsTypeVBox);
3336
3337 /* VM name */
3338 Utf8Str strVMName(bstrName);
3339 pNewDesc->addEntry(VirtualSystemDescriptionType_Name,
3340 "",
3341 strVMName,
3342 strVMName);
3343
3344 /* CPU count*/
3345 Utf8Str strCpuCount = Utf8StrFmt("%RI32", cCPUs);
3346 pNewDesc->addEntry(VirtualSystemDescriptionType_CPU,
3347 "",
3348 strCpuCount,
3349 strCpuCount);
3350
3351 /* Memory */
3352 Utf8Str strMemory = Utf8StrFmt("%RI32", (uint64_t)ulMemSizeMB * _1M);
3353 pNewDesc->addEntry(VirtualSystemDescriptionType_Memory,
3354 "",
3355 strMemory,
3356 strMemory);
3357
3358 int32_t lIDEControllerIndex = 0;
3359 int32_t lSATAControllerIndex = 0;
3360 int32_t lSCSIControllerIndex = 0;
3361
3362// <const name="HardDiskControllerIDE" value="6" />
3363 ComPtr<IStorageController> pController;
3364 rc = GetStorageControllerByName(Bstr("IDE"), pController.asOutParam());
3365 if (FAILED(rc)) throw rc;
3366 Utf8Str strVbox;
3367 StorageControllerType_T ctlr;
3368 rc = pController->COMGETTER(ControllerType)(&ctlr);
3369 if (FAILED(rc)) throw rc;
3370 switch(ctlr)
3371 {
3372 case StorageControllerType_PIIX3: strVbox = "PIIX3"; break;
3373 case StorageControllerType_PIIX4: strVbox = "PIIX4"; break;
3374 case StorageControllerType_ICH6: strVbox = "ICH6"; break;
3375 }
3376
3377 if (strVbox.length())
3378 {
3379 lIDEControllerIndex = (int32_t)pNewDesc->m->llDescriptions.size();
3380 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerIDE,
3381 Utf8StrFmt("%d", lIDEControllerIndex),
3382 strVbox,
3383 "");
3384 }
3385
3386#ifdef VBOX_WITH_AHCI
3387// <const name="HardDiskControllerSATA" value="7" />
3388 rc = GetStorageControllerByName(Bstr("IDE"), pController.asOutParam());
3389 if (SUCCEEDED(rc))
3390 {
3391 lSATAControllerIndex = (int32_t)pNewDesc->m->llDescriptions.size();
3392 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSATA,
3393 Utf8StrFmt("%d", lSATAControllerIndex),
3394 strVbox,
3395 "");
3396 }
3397#endif // VBOX_WITH_AHCI
3398
3399// <const name="HardDiskControllerSCSI" value="8" />
3400 rc = GetStorageControllerByName(Bstr("SCSI"), pController.asOutParam());
3401 if (SUCCEEDED(rc))
3402 {
3403 lSCSIControllerIndex = (int32_t)pNewDesc->m->llDescriptions.size();
3404 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSCSI,
3405 Utf8StrFmt("%d", lSCSIControllerIndex),
3406 strVbox,
3407 "");
3408 }
3409
3410// <const name="HardDiskImage" value="9" />
3411 HDData::AttachmentList::iterator itA;
3412 for (itA = mHDData->mAttachments.begin();
3413 itA != mHDData->mAttachments.end();
3414 ++itA)
3415 {
3416 ComObjPtr<HardDiskAttachment> pHDA = *itA;
3417
3418 // the attachment's data
3419 ComPtr<IHardDisk> pHardDisk;
3420 ComPtr<IStorageController> ctl;
3421 Bstr controllerName;
3422
3423 rc = pHDA->COMGETTER(Controller)(controllerName.asOutParam());
3424 if (FAILED(rc)) throw rc;
3425
3426 rc = GetStorageControllerByName(controllerName, ctl.asOutParam());
3427 if (FAILED(rc)) throw rc;
3428
3429 StorageBus_T storageBus;
3430 LONG lChannel;
3431 LONG lDevice;
3432
3433 rc = ctl->COMGETTER(Bus)(&storageBus);
3434 if (FAILED(rc)) throw rc;
3435
3436 rc = pHDA->COMGETTER(HardDisk)(pHardDisk.asOutParam());
3437 if (FAILED(rc)) throw rc;
3438
3439 rc = pHDA->COMGETTER(Port)(&lChannel);
3440 if (FAILED(rc)) throw rc;
3441
3442 rc = pHDA->COMGETTER(Device)(&lDevice);
3443 if (FAILED(rc)) throw rc;
3444
3445 Bstr bstrLocation;
3446 rc = pHardDisk->COMGETTER(Location)(bstrLocation.asOutParam());
3447 Bstr bstrName;
3448 rc = pHardDisk->COMGETTER(Name)(bstrName.asOutParam());
3449
3450 // and how this translates to the virtual system
3451 int32_t lControllerVsys = 0;
3452 LONG lChannelVsys;
3453
3454 switch (storageBus)
3455 {
3456 case StorageBus_IDE:
3457 // this is the exact reverse to what we're doing in Appliance::taskThreadImportMachines,
3458 // and it must be updated when that is changed!
3459
3460 if (lChannel == 0 && lDevice == 0) // primary master
3461 lChannelVsys = 0;
3462 else if (lChannel == 0 && lDevice == 1) // primary slave
3463 lChannelVsys = 1;
3464 else if (lChannel == 1 && lDevice == 1) // secondary slave; secondary master is always CDROM
3465 lChannelVsys = 2;
3466 else
3467 throw setError(VBOX_E_NOT_SUPPORTED,
3468 tr("Cannot handle hard disk attachment: channel is %d, device is %d"), lChannel, lDevice);
3469
3470 lControllerVsys = lIDEControllerIndex;
3471 break;
3472
3473 case StorageBus_SATA:
3474 lChannelVsys = lChannel; // should be between 0 and 29
3475 lControllerVsys = lSATAControllerIndex;
3476 break;
3477
3478// case StorageBus::SCSI:
3479// // mhda.busType = StorageBus_SCSI;
3480// throw setError(VBOX_E_NOT_SUPPORTED,
3481// tr("SCSI controller support is not available yet in VirtualBox"));
3482// // @todo
3483// break;
3484
3485 default:
3486 throw setError(VBOX_E_NOT_SUPPORTED,
3487 tr("Cannot handle hard disk attachment: storageBus is %d, channel is %d, device is %d"), storageBus, lChannel, lDevice);
3488 break;
3489 }
3490
3491 Utf8Str strTargetVmdkName(bstrName);
3492 RTPathStripExt(strTargetVmdkName.mutableRaw());
3493 strTargetVmdkName.append(".vmdk");
3494
3495 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskImage,
3496 strTargetVmdkName, // disk ID: let's use the name
3497 strTargetVmdkName, // OVF value:
3498 Utf8Str(bstrLocation), // vbox value: media path
3499 Utf8StrFmt("controller=%RI32;channel=%RI32", lControllerVsys, lChannelVsys));
3500 }
3501
3502 /* Floppy Drive */
3503 if (fFloppyEnabled)
3504 pNewDesc->addEntry(VirtualSystemDescriptionType_Floppy, "", "", "");
3505
3506 /* CD Drive */
3507 if (fDVDEnabled)
3508 pNewDesc->addEntry(VirtualSystemDescriptionType_CDROM, "", "", "");
3509
3510// <const name="NetworkAdapter" />
3511 size_t a;
3512 for (a = 0;
3513 a < SchemaDefs::NetworkAdapterCount;
3514 ++a)
3515 {
3516 ComPtr<INetworkAdapter> pNetworkAdapter;
3517 BOOL fEnabled;
3518 NetworkAdapterType_T adapterType;
3519 NetworkAttachmentType_T attachmentType;
3520
3521 rc = GetNetworkAdapter((ULONG)a, pNetworkAdapter.asOutParam());
3522 if (FAILED(rc)) throw rc;
3523 /* Enable the network card & set the adapter type */
3524 rc = pNetworkAdapter->COMGETTER(Enabled)(&fEnabled);
3525 if (FAILED(rc)) throw rc;
3526
3527 if (fEnabled)
3528 {
3529 Utf8Str strAttachmentType;
3530
3531 rc = pNetworkAdapter->COMGETTER(AdapterType)(&adapterType);
3532 if (FAILED(rc)) throw rc;
3533
3534 rc = pNetworkAdapter->COMGETTER(AttachmentType)(&attachmentType);
3535 if (FAILED(rc)) throw rc;
3536
3537 switch (attachmentType)
3538 {
3539 case NetworkAttachmentType_Null:
3540 strAttachmentType = "Null";
3541 break;
3542
3543 case NetworkAttachmentType_NAT:
3544 strAttachmentType = "NAT";
3545 break;
3546
3547 case NetworkAttachmentType_Bridged:
3548 strAttachmentType = "Bridged";
3549 break;
3550
3551 case NetworkAttachmentType_Internal:
3552 strAttachmentType = "Internal";
3553 break;
3554
3555 case NetworkAttachmentType_HostOnly:
3556 strAttachmentType = "HostOnly";
3557 break;
3558 }
3559
3560 pNewDesc->addEntry(VirtualSystemDescriptionType_NetworkAdapter,
3561 "", // ref
3562 strAttachmentType, // orig
3563 Utf8StrFmt("%RI32", (uint32_t)adapterType), // conf
3564 Utf8StrFmt("type=%s", strAttachmentType.c_str())); // extra conf
3565 }
3566 }
3567
3568// <const name="USBController" />
3569#ifdef VBOX_WITH_USB
3570 if (fUSBEnabled)
3571 pNewDesc->addEntry(VirtualSystemDescriptionType_USBController, "", "", "");
3572#endif /* VBOX_WITH_USB */
3573
3574// <const name="SoundCard" />
3575 if (fAudioEnabled)
3576 {
3577 pNewDesc->addEntry(VirtualSystemDescriptionType_SoundCard,
3578 "",
3579 "ensoniq1371", // this is what OVFTool writes and VMware supports
3580 Utf8StrFmt("%RI32", audioController));
3581 }
3582
3583 // finally, add the virtual system to the appliance
3584 Appliance *pAppliance = static_cast<Appliance*>(appliance);
3585 AutoCaller autoCaller(pAppliance);
3586 if (FAILED(rc)) throw rc;
3587
3588 AutoWriteLock alock(pAppliance);
3589
3590 pAppliance->m->virtualSystemDescriptions.push_back(pNewDesc);
3591 }
3592 catch(HRESULT arc)
3593 {
3594 rc = arc;
3595 }
3596
3597 return rc;
3598}
3599
3600/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

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