VirtualBox

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

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

OVF: fixes for empty disks creation

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