VirtualBox

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

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

OVF: fix windows burns

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