VirtualBox

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

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

OVF: added getValuesByType & the license text to the describtions

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

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