VirtualBox

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

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

OVF: make it simpler

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

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