VirtualBox

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

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

Main: turn read/write param in OpenHardDisk into an enum

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

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