VirtualBox

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

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

OVF: fix windows burn

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