VirtualBox

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

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

OVF: replace stringstreams with IPRT funcs

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