VirtualBox

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

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

OVF: move warnings from IVirtualSystem to IAppliance; fix error messages when OVF wants more than one SCSI controller; API docs

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 154.0 KB
Line 
1/* $Id: ApplianceImpl.cpp 17827 2009-03-13 14:14:08Z 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.215389.xyz. 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 /* Based on the file extensions we choose the right format for the
2903 * disk */
2904 Bstr bstrSrcFormat = L"VDI";
2905 if (strTargetFilePath.endsWith(".vmdk", Utf8Str::CaseInsensitive))
2906 bstrSrcFormat = L"VMDK";
2907 /* Create a new hard disk interface for the destination disk image */
2908 Log(("Creating target disk \"%s\"\n", strTargetFilePath.raw()));
2909 rc = pVirtualBox->CreateHardDisk(bstrSrcFormat, Bstr(strTargetFilePath), pTargetDisk.asOutParam());
2910 if (FAILED(rc)) throw rc;
2911 // clone the source disk image
2912 rc = pSourceDisk->CloneTo(pTargetDisk, HardDiskVariant_Standard, pProgress2.asOutParam());
2913 if (FAILED(rc)) throw rc;
2914
2915 // advance to the next operation
2916 if (!task->progress.isNull())
2917 task->progress->advanceOperation(BstrFmt(tr("Exporting virtual disk image '%s'"), strSrcFilePath.c_str()));
2918
2919 // now loop until the asynchronous operation completes and then
2920 // report its result
2921 BOOL fCompleted;
2922 LONG currentPercent;
2923 while (SUCCEEDED(pProgress2->COMGETTER(Completed(&fCompleted))))
2924 {
2925 rc = pProgress2->COMGETTER(Percent(&currentPercent));
2926 if (FAILED(rc)) throw rc;
2927 if (!task->progress.isNull())
2928 task->progress->notifyProgress(currentPercent);
2929 if (fCompleted)
2930 break;
2931 // make sure the loop is not too tight
2932 rc = pProgress2->WaitForCompletion(100);
2933 if (FAILED(rc)) throw rc;
2934 }
2935 // report result of asynchronous operation
2936 HRESULT vrc;
2937 rc = pProgress2->COMGETTER(ResultCode)(&vrc);
2938 if (FAILED(rc)) throw rc;
2939
2940 // if the thread of the progress object has an error, then
2941 // retrieve the error info from there, or it'll be lost
2942 if (FAILED(vrc))
2943 {
2944 ProgressErrorInfo info(pProgress2);
2945 Utf8Str str(info.getText());
2946 const char *pcsz = str.c_str();
2947 HRESULT rc2 = setError(vrc, pcsz);
2948 throw rc2;
2949 }
2950
2951 /* Make sure the target disk get detached */
2952 rc = pTargetDisk->Close(); // @todo close this also if an error is thrown above
2953 if (FAILED(rc)) throw rc;
2954
2955 // we need the capacity and actual file size for the XML
2956 uint64_t cbFile = 12345678; // @todo
2957 uint64_t cbCapacity = 2345678; // @todo
2958
2959 // now handle the XML for the disk:
2960 Utf8StrFmt strFileRef("file%RI32", ulFile++);
2961 // <File ovf:href="WindowsXpProfessional-disk1.vmdk" ovf:id="file1" ovf:size="1710381056"/>
2962 xml::ElementNode *pelmFile = pelmReferences->createChild("File");
2963 pelmFile->setAttribute("ovf:href", strTargetFileNameOnly);
2964 pelmFile->setAttribute("ovf:id", strFileRef);
2965 pelmFile->setAttribute("ovf:size", Utf8StrFmt("%RI64", cbFile).c_str()); // @todo
2966
2967 // add disk to XML Disks section
2968 // <Disk ovf:capacity="8589934592" ovf:diskId="vmdisk1" ovf:fileRef="file1" ovf:format="http://www.vmware.com/specifications/vmdk.html#sparse"/>
2969 xml::ElementNode *pelmDisk = pelmDiskSection->createChild("Disk");
2970 pelmDisk->setAttribute("ovf:capacity", Utf8StrFmt("%RI64", cbCapacity).c_str()); // @todo
2971 pelmDisk->setAttribute("ovf:diskId", strDiskID);
2972 pelmDisk->setAttribute("ovf:fileRef", strFileRef);
2973 pelmDisk->setAttribute("ovf:format", "http://www.vmware.com/specifications/vmdk.html#sparse");
2974 }
2975
2976 // now go write the XML
2977 xml::XmlFileWriter writer(doc);
2978 writer.write(pAppliance->m->strPath.c_str());
2979 }
2980 catch(xml::Error &x)
2981 {
2982 rc = setError(VBOX_E_FILE_ERROR,
2983 x.what());
2984 }
2985 catch(HRESULT aRC)
2986 {
2987 rc = aRC;
2988 }
2989
2990 task->rc = rc;
2991
2992 if (!task->progress.isNull())
2993 task->progress->notifyComplete(rc);
2994
2995 LogFlowFunc(("rc=%Rhrc\n", rc));
2996 LogFlowFuncLeave();
2997
2998 return VINF_SUCCESS;
2999}
3000
3001////////////////////////////////////////////////////////////////////////////////
3002//
3003// IVirtualSystemDescription constructor / destructor
3004//
3005////////////////////////////////////////////////////////////////////////////////
3006
3007DEFINE_EMPTY_CTOR_DTOR(VirtualSystemDescription)
3008struct shutup3 {};
3009
3010/**
3011 * COM initializer.
3012 * @return
3013 */
3014HRESULT VirtualSystemDescription::init()
3015{
3016 /* Enclose the state transition NotReady->InInit->Ready */
3017 AutoInitSpan autoInitSpan(this);
3018 AssertReturn(autoInitSpan.isOk(), E_FAIL);
3019
3020 /* Initialize data */
3021 m = new Data();
3022
3023 /* Confirm a successful initialization */
3024 autoInitSpan.setSucceeded();
3025 return S_OK;
3026}
3027
3028/**
3029* COM uninitializer.
3030*/
3031
3032void VirtualSystemDescription::uninit()
3033{
3034 delete m;
3035 m = NULL;
3036}
3037
3038////////////////////////////////////////////////////////////////////////////////
3039//
3040// IVirtualSystemDescription public methods
3041//
3042////////////////////////////////////////////////////////////////////////////////
3043
3044/**
3045 * Public method implementation.
3046 * @param
3047 * @return
3048 */
3049STDMETHODIMP VirtualSystemDescription::COMGETTER(Count)(ULONG *aCount)
3050{
3051 if (!aCount)
3052 return E_POINTER;
3053
3054 AutoCaller autoCaller(this);
3055 CheckComRCReturnRC(autoCaller.rc());
3056
3057 AutoReadLock alock(this);
3058
3059 *aCount = (ULONG)m->llDescriptions.size();
3060
3061 return S_OK;
3062}
3063
3064/**
3065 * Public method implementation.
3066 * @return
3067 */
3068STDMETHODIMP VirtualSystemDescription::GetDescription(ComSafeArrayOut(VirtualSystemDescriptionType_T, aTypes),
3069 ComSafeArrayOut(BSTR, aRefs),
3070 ComSafeArrayOut(BSTR, aOrigValues),
3071 ComSafeArrayOut(BSTR, aVboxValues),
3072 ComSafeArrayOut(BSTR, aExtraConfigValues))
3073{
3074 if (ComSafeArrayOutIsNull(aTypes) ||
3075 ComSafeArrayOutIsNull(aRefs) ||
3076 ComSafeArrayOutIsNull(aOrigValues) ||
3077 ComSafeArrayOutIsNull(aVboxValues) ||
3078 ComSafeArrayOutIsNull(aExtraConfigValues))
3079 return E_POINTER;
3080
3081 AutoCaller autoCaller(this);
3082 CheckComRCReturnRC(autoCaller.rc());
3083
3084 AutoReadLock alock(this);
3085
3086 ULONG c = (ULONG)m->llDescriptions.size();
3087 com::SafeArray<VirtualSystemDescriptionType_T> sfaTypes(c);
3088 com::SafeArray<BSTR> sfaRefs(c);
3089 com::SafeArray<BSTR> sfaOrigValues(c);
3090 com::SafeArray<BSTR> sfaVboxValues(c);
3091 com::SafeArray<BSTR> sfaExtraConfigValues(c);
3092
3093 list<VirtualSystemDescriptionEntry>::const_iterator it;
3094 size_t i = 0;
3095 for (it = m->llDescriptions.begin();
3096 it != m->llDescriptions.end();
3097 ++it, ++i)
3098 {
3099 const VirtualSystemDescriptionEntry &vsde = (*it);
3100
3101 sfaTypes[i] = vsde.type;
3102
3103 Bstr bstr = vsde.strRef;
3104 bstr.cloneTo(&sfaRefs[i]);
3105
3106 bstr = vsde.strOvf;
3107 bstr.cloneTo(&sfaOrigValues[i]);
3108
3109 bstr = vsde.strVbox;
3110 bstr.cloneTo(&sfaVboxValues[i]);
3111
3112 bstr = vsde.strExtraConfig;
3113 bstr.cloneTo(&sfaExtraConfigValues[i]);
3114 }
3115
3116 sfaTypes.detachTo(ComSafeArrayOutArg(aTypes));
3117 sfaRefs.detachTo(ComSafeArrayOutArg(aRefs));
3118 sfaOrigValues.detachTo(ComSafeArrayOutArg(aOrigValues));
3119 sfaVboxValues.detachTo(ComSafeArrayOutArg(aVboxValues));
3120 sfaExtraConfigValues.detachTo(ComSafeArrayOutArg(aExtraConfigValues));
3121
3122 return S_OK;
3123}
3124
3125/**
3126 * Public method implementation.
3127 * @return
3128 */
3129STDMETHODIMP VirtualSystemDescription::SetFinalValues(ComSafeArrayIn(BOOL, aEnabled),
3130 ComSafeArrayIn(IN_BSTR, argVboxValues),
3131 ComSafeArrayIn(IN_BSTR, argExtraConfigValues))
3132{
3133 CheckComArgSafeArrayNotNull(argVboxValues);
3134 CheckComArgSafeArrayNotNull(argExtraConfigValues);
3135
3136 AutoCaller autoCaller(this);
3137 CheckComRCReturnRC(autoCaller.rc());
3138
3139 AutoWriteLock alock(this);
3140
3141 com::SafeArray<IN_BSTR> aVboxValues(ComSafeArrayInArg(argVboxValues));
3142 com::SafeArray<IN_BSTR> aExtraConfigValues(ComSafeArrayInArg(argExtraConfigValues));
3143
3144 if ( (aVboxValues.size() != m->llDescriptions.size())
3145 || (aExtraConfigValues.size() != m->llDescriptions.size())
3146 )
3147 return E_INVALIDARG;
3148
3149 list<VirtualSystemDescriptionEntry>::iterator it;
3150 size_t i = 0;
3151 for (it = m->llDescriptions.begin();
3152 it != m->llDescriptions.end();
3153 ++it, ++i)
3154 {
3155 VirtualSystemDescriptionEntry& vsde = *it;
3156
3157 if (aEnabled[i])
3158 {
3159 vsde.strVbox = aVboxValues[i];
3160 vsde.strExtraConfig = aExtraConfigValues[i];
3161 }
3162 else
3163 vsde.type = VirtualSystemDescriptionType_Ignore;
3164 }
3165
3166 return S_OK;
3167}
3168
3169/**
3170 * Internal method; adds a new description item to the member list.
3171 * @param aType Type of description for the new item.
3172 * @param strRef Reference item; only used with hard disk controllers.
3173 * @param aOrigValue Corresponding original value from OVF.
3174 * @param aAutoValue Initial configuration value (can be overridden by caller with setFinalValues).
3175 * @param strExtraConfig Extra configuration; meaning dependent on type.
3176 */
3177void VirtualSystemDescription::addEntry(VirtualSystemDescriptionType_T aType,
3178 const Utf8Str &strRef,
3179 const Utf8Str &aOrigValue,
3180 const Utf8Str &aAutoValue,
3181 const Utf8Str &strExtraConfig /*= ""*/)
3182{
3183 VirtualSystemDescriptionEntry vsde;
3184 vsde.ulIndex = (uint32_t)m->llDescriptions.size(); // each entry gets an index so the client side can reference them
3185 vsde.type = aType;
3186 vsde.strRef = strRef;
3187 vsde.strOvf = aOrigValue;
3188 vsde.strVbox = aAutoValue;
3189 vsde.strExtraConfig = strExtraConfig;
3190
3191 m->llDescriptions.push_back(vsde);
3192}
3193
3194/**
3195 * Private method; returns a list of description items containing all the items from the member
3196 * description items of this virtual system that match the given type.
3197 * @param aType
3198 * @return
3199 */
3200std::list<VirtualSystemDescriptionEntry*> VirtualSystemDescription::findByType(VirtualSystemDescriptionType_T aType)
3201{
3202 std::list<VirtualSystemDescriptionEntry*> vsd;
3203
3204 list<VirtualSystemDescriptionEntry>::iterator it;
3205 for (it = m->llDescriptions.begin();
3206 it != m->llDescriptions.end();
3207 ++it)
3208 {
3209 if (it->type == aType)
3210 vsd.push_back(&(*it));
3211 }
3212
3213 return vsd;
3214}
3215
3216/**
3217 * Private method; looks thru the member hardware items for the IDE, SATA, or SCSI controller with
3218 * the given reference ID. Useful when needing the controller for a particular
3219 * virtual disk.
3220 * @param id
3221 * @return
3222 */
3223const VirtualSystemDescriptionEntry* VirtualSystemDescription::findControllerFromID(uint32_t id)
3224{
3225 Utf8Str strRef = Utf8StrFmt("%RI32", id);
3226 list<VirtualSystemDescriptionEntry>::const_iterator it;
3227 for (it = m->llDescriptions.begin();
3228 it != m->llDescriptions.end();
3229 ++it)
3230 {
3231 const VirtualSystemDescriptionEntry &d = *it;
3232 switch (d.type)
3233 {
3234 case VirtualSystemDescriptionType_HardDiskControllerIDE:
3235 case VirtualSystemDescriptionType_HardDiskControllerSATA:
3236 case VirtualSystemDescriptionType_HardDiskControllerSCSI:
3237 if (d.strRef == strRef)
3238 return &d;
3239 break;
3240 }
3241 }
3242
3243 return NULL;
3244}
3245
3246////////////////////////////////////////////////////////////////////////////////
3247//
3248// IMachine public methods
3249//
3250////////////////////////////////////////////////////////////////////////////////
3251
3252// This code is here so we won't have to include the appliance headers in the
3253// IMachine implementation, and we also need to access private appliance data.
3254
3255/**
3256* Public method implementation.
3257* @param appliance
3258* @return
3259*/
3260
3261STDMETHODIMP Machine::Export(IAppliance *appliance)
3262{
3263 HRESULT rc = S_OK;
3264
3265 if (!appliance)
3266 return E_POINTER;
3267
3268 AutoCaller autoCaller(this);
3269 CheckComRCReturnRC(autoCaller.rc());
3270
3271 AutoReadLock alock(this);
3272
3273 ComObjPtr<VirtualSystemDescription> pNewDesc;
3274
3275 try
3276 {
3277 Bstr bstrName;
3278 Bstr bstrDescription;
3279 Bstr bstrGuestOSType;
3280 uint32_t cCPUs;
3281 uint32_t ulMemSizeMB;
3282 BOOL fDVDEnabled;
3283 BOOL fFloppyEnabled;
3284 BOOL fUSBEnabled;
3285 BOOL fAudioEnabled;
3286 AudioControllerType_T audioController;
3287
3288 ComPtr<IUSBController> pUsbController;
3289 ComPtr<IAudioAdapter> pAudioAdapter;
3290
3291 // get name
3292 bstrName = mUserData->mName;
3293 // get description
3294 bstrDescription = mUserData->mDescription;
3295 // get guest OS
3296 bstrGuestOSType = mUserData->mOSTypeId;
3297 // CPU count
3298 cCPUs = mHWData->mCPUCount;
3299 // memory size in MB
3300 ulMemSizeMB = mHWData->mMemorySize;
3301 // VRAM size?
3302 // BIOS settings?
3303 // 3D acceleration enabled?
3304 // hardware virtualization enabled?
3305 // nested paging enabled?
3306 // HWVirtExVPIDEnabled?
3307 // PAEEnabled?
3308 // snapshotFolder?
3309 // VRDPServer?
3310
3311 // floppy
3312 rc = mFloppyDrive->COMGETTER(Enabled)(&fFloppyEnabled);
3313 if (FAILED(rc)) throw rc;
3314
3315 // CD-ROM ?!?
3316 // ComPtr<IDVDDrive> pDVDDrive;
3317 fDVDEnabled = 1;
3318
3319 // this is more tricky so use the COM method
3320 rc = COMGETTER(USBController)(pUsbController.asOutParam());
3321 if (FAILED(rc)) throw rc;
3322 rc = pUsbController->COMGETTER(Enabled)(&fUSBEnabled);
3323
3324 pAudioAdapter = mAudioAdapter;
3325 rc = pAudioAdapter->COMGETTER(Enabled)(&fAudioEnabled);
3326 if (FAILED(rc)) throw rc;
3327 rc = pAudioAdapter->COMGETTER(AudioController)(&audioController);
3328 if (FAILED(rc)) throw rc;
3329
3330 // create a new virtual system
3331 rc = pNewDesc.createObject();
3332 CheckComRCThrowRC(rc);
3333 rc = pNewDesc->init();
3334 CheckComRCThrowRC(rc);
3335
3336 /* Guest OS type */
3337 Utf8Str strOsTypeVBox(bstrGuestOSType);
3338 CIMOSType_T cim = convertVBoxOSType2CIMOSType(strOsTypeVBox.c_str());
3339 pNewDesc->addEntry(VirtualSystemDescriptionType_OS,
3340 "",
3341 Utf8StrFmt("%RI32", cim),
3342 strOsTypeVBox);
3343
3344 /* VM name */
3345 Utf8Str strVMName(bstrName);
3346 pNewDesc->addEntry(VirtualSystemDescriptionType_Name,
3347 "",
3348 strVMName,
3349 strVMName);
3350
3351 /* CPU count*/
3352 Utf8Str strCpuCount = Utf8StrFmt("%RI32", cCPUs);
3353 pNewDesc->addEntry(VirtualSystemDescriptionType_CPU,
3354 "",
3355 strCpuCount,
3356 strCpuCount);
3357
3358 /* Memory */
3359 Utf8Str strMemory = Utf8StrFmt("%RI32", (uint64_t)ulMemSizeMB * _1M);
3360 pNewDesc->addEntry(VirtualSystemDescriptionType_Memory,
3361 "",
3362 strMemory,
3363 strMemory);
3364
3365 int32_t lIDEControllerIndex = 0;
3366 int32_t lSATAControllerIndex = 0;
3367 int32_t lSCSIControllerIndex = 0;
3368
3369// <const name="HardDiskControllerIDE" value="6" />
3370 ComPtr<IStorageController> pController;
3371 rc = GetStorageControllerByName(Bstr("IDE"), pController.asOutParam());
3372 if (FAILED(rc)) throw rc;
3373 Utf8Str strVbox;
3374 StorageControllerType_T ctlr;
3375 rc = pController->COMGETTER(ControllerType)(&ctlr);
3376 if (FAILED(rc)) throw rc;
3377 switch(ctlr)
3378 {
3379 case StorageControllerType_PIIX3: strVbox = "PIIX3"; break;
3380 case StorageControllerType_PIIX4: strVbox = "PIIX4"; break;
3381 case StorageControllerType_ICH6: strVbox = "ICH6"; break;
3382 }
3383
3384 if (strVbox.length())
3385 {
3386 lIDEControllerIndex = (int32_t)pNewDesc->m->llDescriptions.size();
3387 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerIDE,
3388 Utf8StrFmt("%d", lIDEControllerIndex),
3389 strVbox,
3390 "");
3391 }
3392
3393#ifdef VBOX_WITH_AHCI
3394// <const name="HardDiskControllerSATA" value="7" />
3395 rc = GetStorageControllerByName(Bstr("IDE"), pController.asOutParam());
3396 strVbox = "";
3397 if (SUCCEEDED(rc))
3398 {
3399 lSATAControllerIndex = (int32_t)pNewDesc->m->llDescriptions.size();
3400 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSATA,
3401 Utf8StrFmt("%d", lSATAControllerIndex),
3402 strVbox,
3403 "");
3404 }
3405#endif // VBOX_WITH_AHCI
3406
3407// <const name="HardDiskControllerSCSI" value="8" />
3408 rc = GetStorageControllerByName(Bstr("SCSI"), pController.asOutParam());
3409 rc = pController->COMGETTER(ControllerType)(&ctlr);
3410 if (FAILED(rc)) throw rc;
3411 strVbox = "LsiLogic"; // the default in VBox
3412 switch(ctlr)
3413 {
3414 case StorageControllerType_LsiLogic: strVbox = "LsiLogic"; break;
3415 case StorageControllerType_BusLogic: strVbox = "BusLogic"; break;
3416 }
3417 if (SUCCEEDED(rc))
3418 {
3419 lSCSIControllerIndex = (int32_t)pNewDesc->m->llDescriptions.size();
3420 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSCSI,
3421 Utf8StrFmt("%d", lSCSIControllerIndex),
3422 strVbox,
3423 "");
3424 }
3425
3426// <const name="HardDiskImage" value="9" />
3427 HDData::AttachmentList::iterator itA;
3428 for (itA = mHDData->mAttachments.begin();
3429 itA != mHDData->mAttachments.end();
3430 ++itA)
3431 {
3432 ComObjPtr<HardDiskAttachment> pHDA = *itA;
3433
3434 // the attachment's data
3435 ComPtr<IHardDisk> pHardDisk;
3436 ComPtr<IStorageController> ctl;
3437 Bstr controllerName;
3438
3439 rc = pHDA->COMGETTER(Controller)(controllerName.asOutParam());
3440 if (FAILED(rc)) throw rc;
3441
3442 rc = GetStorageControllerByName(controllerName, ctl.asOutParam());
3443 if (FAILED(rc)) throw rc;
3444
3445 StorageBus_T storageBus;
3446 LONG lChannel;
3447 LONG lDevice;
3448
3449 rc = ctl->COMGETTER(Bus)(&storageBus);
3450 if (FAILED(rc)) throw rc;
3451
3452 rc = pHDA->COMGETTER(HardDisk)(pHardDisk.asOutParam());
3453 if (FAILED(rc)) throw rc;
3454
3455 rc = pHDA->COMGETTER(Port)(&lChannel);
3456 if (FAILED(rc)) throw rc;
3457
3458 rc = pHDA->COMGETTER(Device)(&lDevice);
3459 if (FAILED(rc)) throw rc;
3460
3461 Bstr bstrLocation;
3462 rc = pHardDisk->COMGETTER(Location)(bstrLocation.asOutParam());
3463 Bstr bstrName;
3464 rc = pHardDisk->COMGETTER(Name)(bstrName.asOutParam());
3465
3466 // and how this translates to the virtual system
3467 int32_t lControllerVsys = 0;
3468 LONG lChannelVsys;
3469
3470 switch (storageBus)
3471 {
3472 case StorageBus_IDE:
3473 // this is the exact reverse to what we're doing in Appliance::taskThreadImportMachines,
3474 // and it must be updated when that is changed!
3475
3476 if (lChannel == 0 && lDevice == 0) // primary master
3477 lChannelVsys = 0;
3478 else if (lChannel == 0 && lDevice == 1) // primary slave
3479 lChannelVsys = 1;
3480 else if (lChannel == 1 && lDevice == 1) // secondary slave; secondary master is always CDROM
3481 lChannelVsys = 2;
3482 else
3483 throw setError(VBOX_E_NOT_SUPPORTED,
3484 tr("Cannot handle hard disk attachment: channel is %d, device is %d"), lChannel, lDevice);
3485
3486 lControllerVsys = lIDEControllerIndex;
3487 break;
3488
3489 case StorageBus_SATA:
3490 lChannelVsys = lChannel; // should be between 0 and 29
3491 lControllerVsys = lSATAControllerIndex;
3492 break;
3493
3494// case StorageBus::SCSI:
3495// // mhda.busType = StorageBus_SCSI;
3496// throw setError(VBOX_E_NOT_SUPPORTED,
3497// tr("SCSI controller support is not available yet in VirtualBox"));
3498// // @todo
3499// break;
3500
3501 default:
3502 throw setError(VBOX_E_NOT_SUPPORTED,
3503 tr("Cannot handle hard disk attachment: storageBus is %d, channel is %d, device is %d"), storageBus, lChannel, lDevice);
3504 break;
3505 }
3506
3507 Utf8Str strTargetVmdkName(bstrName);
3508 RTPathStripExt(strTargetVmdkName.mutableRaw());
3509 strTargetVmdkName.append(".vmdk");
3510
3511 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskImage,
3512 strTargetVmdkName, // disk ID: let's use the name
3513 strTargetVmdkName, // OVF value:
3514 Utf8Str(bstrLocation), // vbox value: media path
3515 Utf8StrFmt("controller=%RI32;channel=%RI32", lControllerVsys, lChannelVsys));
3516 }
3517
3518 /* Floppy Drive */
3519 if (fFloppyEnabled)
3520 pNewDesc->addEntry(VirtualSystemDescriptionType_Floppy, "", "", "");
3521
3522 /* CD Drive */
3523 if (fDVDEnabled)
3524 pNewDesc->addEntry(VirtualSystemDescriptionType_CDROM, "", "", "");
3525
3526// <const name="NetworkAdapter" />
3527 size_t a;
3528 for (a = 0;
3529 a < SchemaDefs::NetworkAdapterCount;
3530 ++a)
3531 {
3532 ComPtr<INetworkAdapter> pNetworkAdapter;
3533 BOOL fEnabled;
3534 NetworkAdapterType_T adapterType;
3535 NetworkAttachmentType_T attachmentType;
3536
3537 rc = GetNetworkAdapter((ULONG)a, pNetworkAdapter.asOutParam());
3538 if (FAILED(rc)) throw rc;
3539 /* Enable the network card & set the adapter type */
3540 rc = pNetworkAdapter->COMGETTER(Enabled)(&fEnabled);
3541 if (FAILED(rc)) throw rc;
3542
3543 if (fEnabled)
3544 {
3545 Utf8Str strAttachmentType;
3546
3547 rc = pNetworkAdapter->COMGETTER(AdapterType)(&adapterType);
3548 if (FAILED(rc)) throw rc;
3549
3550 rc = pNetworkAdapter->COMGETTER(AttachmentType)(&attachmentType);
3551 if (FAILED(rc)) throw rc;
3552
3553 switch (attachmentType)
3554 {
3555 case NetworkAttachmentType_Null:
3556 strAttachmentType = "Null";
3557 break;
3558
3559 case NetworkAttachmentType_NAT:
3560 strAttachmentType = "NAT";
3561 break;
3562
3563 case NetworkAttachmentType_Bridged:
3564 strAttachmentType = "Bridged";
3565 break;
3566
3567 case NetworkAttachmentType_Internal:
3568 strAttachmentType = "Internal";
3569 break;
3570
3571 case NetworkAttachmentType_HostOnly:
3572 strAttachmentType = "HostOnly";
3573 break;
3574 }
3575
3576 pNewDesc->addEntry(VirtualSystemDescriptionType_NetworkAdapter,
3577 "", // ref
3578 strAttachmentType, // orig
3579 Utf8StrFmt("%RI32", (uint32_t)adapterType), // conf
3580 Utf8StrFmt("type=%s", strAttachmentType.c_str())); // extra conf
3581 }
3582 }
3583
3584// <const name="USBController" />
3585#ifdef VBOX_WITH_USB
3586 if (fUSBEnabled)
3587 pNewDesc->addEntry(VirtualSystemDescriptionType_USBController, "", "", "");
3588#endif /* VBOX_WITH_USB */
3589
3590// <const name="SoundCard" />
3591 if (fAudioEnabled)
3592 {
3593 pNewDesc->addEntry(VirtualSystemDescriptionType_SoundCard,
3594 "",
3595 "ensoniq1371", // this is what OVFTool writes and VMware supports
3596 Utf8StrFmt("%RI32", audioController));
3597 }
3598
3599 // finally, add the virtual system to the appliance
3600 Appliance *pAppliance = static_cast<Appliance*>(appliance);
3601 AutoCaller autoCaller(pAppliance);
3602 if (FAILED(rc)) throw rc;
3603
3604 AutoWriteLock alock(pAppliance);
3605
3606 pAppliance->m->virtualSystemDescriptions.push_back(pNewDesc);
3607 }
3608 catch(HRESULT arc)
3609 {
3610 rc = arc;
3611 }
3612
3613 return rc;
3614}
3615
3616/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

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