VirtualBox

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

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

OVF: we are writing compressed disks now

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