VirtualBox

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

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

OVF: prototypes for export.

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