VirtualBox

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

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

OVF: turn IAppliance::getDisks into real attribute, code cleanup, preliminary API documentation.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 62.7 KB
Line 
1/* $Id: ApplianceImpl.cpp 16248 2009-01-26 21:11:36Z 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
26#include "ApplianceImpl.h"
27#include "VirtualBoxImpl.h"
28#include "GuestOSTypeImpl.h"
29
30#include "Logging.h"
31
32#include "VBox/xml.h"
33
34#include <iostream>
35#include <sstream>
36
37using namespace std;
38
39// defines
40////////////////////////////////////////////////////////////////////////////////
41
42struct DiskImage
43{
44 string 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 string 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 string 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 string strCompression; // value from /References/File/@compression (optional, can be "gzip" according to spec)
59};
60
61struct Network
62{
63 string 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 string strDescription;
70 string strCaption;
71 string strElementName;
72
73 uint32_t ulInstanceID;
74 uint32_t ulParent;
75
76 OVFResourceType_T resourceType;
77 string strOtherResourceType;
78 string strResourceSubType;
79
80 string 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 string 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 string strAddress; // "Device-specific. For an Ethernet adapter, this specifies the MAC address."
89 string strAddressOnParent; // "For a device, this specifies its location on the controller."
90 string 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 string strConsumerVisibility;
97 string strMappingBehavior;
98 string 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<string, DiskImage> DiskImagesMap;
109typedef map<string, 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
129enum ControllerSystemType { IDE, SATA, SCSI };
130struct HardDiskController
131{
132 uint32_t idController; // instance ID (Item/InstanceId); this gets referenced from HardDisk
133 ControllerSystemType controllerSystem; // one of IDE, SATA, SCSI
134 string strControllerType; // controller type (Item/ResourceSubType); e.g. "LsiLogic"; can be empty (esp. for IDE)
135 string 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 string 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
156struct VirtualSystem
157{
158 string strName; // copy of VirtualSystem/@id
159
160 CIMOSType_T cimos;
161 string strVirtualSystemType;
162
163 HardwareItemsMap mapHardwareItems; // map of virtual hardware items, sorted by unique instance ID
164
165 uint64_t ullMemorySize; // always in bytes, copied from llHardwareItems; default = 0 (unspecified)
166 uint16_t cCPUs; // no. of CPUs, copied from llHardwareItems; default = 1
167
168 list<string> llNetworkNames;
169 // list of strings referring to network names
170 // (one for each VirtualSystem/Item[@ResourceType=10]/Connection element)
171
172 ControllersMap mapControllers;
173 // list of hard disk controllers
174 // (one for each VirtualSystem/Item[@ResourceType=6] element with accumulated data from children)
175
176 list<VirtualDisk> llVirtualDisks;
177 // (one for each VirtualSystem/Item[@ResourceType=17] element with accumulated data from children)
178
179 string strLicenceInfo; // license info if any; receives contents of VirtualSystem/EulaSection/Info
180 string strLicenceText; // license info if any; receives contents of VirtualSystem/EulaSection/License
181
182 VirtualSystem()
183 : ullMemorySize(0), cCPUs(1)
184 {
185 }
186};
187
188// globals
189////////////////////////////////////////////////////////////////////////////////
190
191template <class T> inline std::string toString(const T& val)
192{
193 std::ostringstream ss;
194 ss << val;
195 return ss.str();
196}
197
198// IVirtualBox public methods
199////////////////////////////////////////////////////////////////////////////////
200
201/**
202 * Implementation for IVirtualBox::openAppliance. Loads the given appliance (see API reference).
203 *
204 * @param bstrPath Appliance to open (either .ovf or .ova file, see API reference)
205 * @param anAppliance IAppliance object created if S_OK is returned.
206 * @return S_OK or error.
207 */
208STDMETHODIMP VirtualBox::OpenAppliance (IN_BSTR bstrPath, IAppliance** anAppliance)
209{
210 HRESULT rc;
211
212 ComObjPtr<Appliance> appliance;
213 appliance.createObject();
214 rc = appliance->init(this, bstrPath);
215// ComAssertComRCThrowRC(rc);
216
217 if (SUCCEEDED(rc))
218 appliance.queryInterfaceTo(anAppliance);
219
220 return rc;
221}
222
223// IAppliance constructor / destructor
224////////////////////////////////////////////////////////////////////////////////
225
226DEFINE_EMPTY_CTOR_DTOR(Appliance)
227struct shutup {};
228
229// IAppliance private methods
230////////////////////////////////////////////////////////////////////////////////
231
232/**
233 * Private helper method that goes thru the elements of the given "current" element in the OVF XML
234 * and handles the contained child elements (which can be "Section" or "Content" elements).
235 *
236 * @param pcszPath Path spec of the XML file, for error messages.
237 * @param pReferencesElement "References" element from OVF, for looking up file specifications; can be NULL if no such element is present.
238 * @param pCurElem Element whose children are to be analyzed here.
239 * @return
240 */
241HRESULT Appliance::LoopThruSections(const char *pcszPath,
242 const xml::Node *pReferencesElem,
243 const xml::Node *pCurElem)
244{
245 HRESULT rc;
246
247 xml::NodesLoop loopChildren(*pCurElem);
248 const xml::Node *pElem;
249 while ((pElem = loopChildren.forAllNodes()))
250 {
251 const char *pcszElemName = pElem->getName();
252 const char *pcszTypeAttr = "";
253 const xml::Node *pTypeAttr;
254 if ((pTypeAttr = pElem->findAttribute("type")))
255 pcszTypeAttr = pTypeAttr->getValue();
256
257 if ( (!strcmp(pcszElemName, "DiskSection"))
258 || ( (!strcmp(pcszElemName, "Section"))
259 && (!strcmp(pcszTypeAttr, "ovf:DiskSection_Type"))
260 )
261 )
262 {
263 if (!(SUCCEEDED((rc = HandleDiskSection(pcszPath, pReferencesElem, pElem)))))
264 return rc;
265 }
266 else if ( (!strcmp(pcszElemName, "NetworkSection"))
267 || ( (!strcmp(pcszElemName, "Section"))
268 && (!strcmp(pcszTypeAttr, "ovf:NetworkSection_Type"))
269 )
270 )
271 {
272 if (!(SUCCEEDED((rc = HandleNetworkSection(pcszPath, pElem)))))
273 return rc;
274 }
275 else if ( (!strcmp(pcszElemName, "DeploymentOptionSection>")))
276 {
277 // TODO
278 }
279 else if ( (!strcmp(pcszElemName, "Info")))
280 {
281 // child of VirtualSystemCollection -- TODO
282 }
283 else if ( (!strcmp(pcszElemName, "ResourceAllocationSection")))
284 {
285 // child of VirtualSystemCollection -- TODO
286 }
287 else if ( (!strcmp(pcszElemName, "StartupSection")))
288 {
289 // child of VirtualSystemCollection -- TODO
290 }
291 else if ( (!strcmp(pcszElemName, "VirtualSystem"))
292 || ( (!strcmp(pcszElemName, "Content"))
293 && (!strcmp(pcszTypeAttr, "ovf:VirtualSystem_Type"))
294 )
295 )
296 {
297 if (!(SUCCEEDED((rc = HandleVirtualSystemContent(pcszPath, pElem)))))
298 return rc;
299 }
300 else if ( (!strcmp(pcszElemName, "VirtualSystemCollection"))
301 || ( (!strcmp(pcszElemName, "Content"))
302 && (!strcmp(pcszTypeAttr, "ovf:VirtualSystemCollection_Type"))
303 )
304 )
305 {
306 // TODO ResourceAllocationSection
307
308 // recurse for this, since it has VirtualSystem elements as children
309 if (!(SUCCEEDED((rc = LoopThruSections(pcszPath, pReferencesElem, pElem)))))
310 return rc;
311 }
312 }
313
314 return S_OK;
315}
316
317/**
318 * Private helper method that handles disk sections in the OVF XML.
319 * @param pcszPath Path spec of the XML file, for error messages.
320 * @param pReferencesElement "References" element from OVF, for looking up file specifications; can be NULL if no such element is present.
321 * @param pSectionElem Section element for which this helper is getting called.
322 * @return
323 */
324HRESULT Appliance::HandleDiskSection(const char *pcszPath,
325 const xml::Node *pReferencesElem,
326 const xml::Node *pSectionElem)
327{
328 // contains "Disk" child elements
329 xml::NodesLoop loopDisks(*pSectionElem, "Disk");
330 const xml::Node *pelmDisk;
331 while ((pelmDisk = loopDisks.forAllNodes()))
332 {
333 DiskImage d;
334 const char *pcszBad = NULL;
335 if (!(pelmDisk->getAttributeValue("diskId", d.strDiskId)))
336 pcszBad = "diskId";
337 else if (!(pelmDisk->getAttributeValue("format", d.strFormat)))
338 pcszBad = "format";
339 else if (!(pelmDisk->getAttributeValue("capacity", d.iCapacity)))
340 pcszBad = "capacity";
341 else
342 {
343 if (!(pelmDisk->getAttributeValue("populatedSize", d.iPopulatedSize)))
344 // optional
345 d.iPopulatedSize = -1;
346
347 string strFileRef;
348 if (pelmDisk->getAttributeValue("fileRef", strFileRef)) // optional
349 {
350 // look up corresponding /References/File nodes (list built above)
351 const xml::Node *pFileElem;
352 if ( pReferencesElem
353 && ((pFileElem = pReferencesElem->findChildElementFromId(strFileRef.c_str())))
354 )
355 {
356 // copy remaining values from file node then
357 const char *pcszBadInFile = NULL;
358 if (!(pFileElem->getAttributeValue("href", d.strHref)))
359 pcszBadInFile = "href";
360 else if (!(pFileElem->getAttributeValue("size", d.iSize)))
361 d.iSize = -1; // optional
362 // if (!(pFileElem->getAttributeValue("size", d.iChunkSize))) TODO
363 d.iChunkSize = -1; // optional
364 pFileElem->getAttributeValue("compression", d.strCompression);
365
366 if (pcszBadInFile)
367 return setError(VBOX_E_FILE_ERROR,
368 tr("Error reading \"%s\": missing or invalid attribute '%s' in 'File' element, line %d"),
369 pcszPath,
370 pcszBadInFile,
371 pFileElem->getLineNumber());
372 }
373 else
374 return setError(VBOX_E_FILE_ERROR,
375 tr("Error reading \"%s\": cannot find References/File element for ID '%s' referenced by 'Disk' element, line %d"),
376 pcszPath,
377 strFileRef.c_str(),
378 pelmDisk->getLineNumber());
379 }
380 }
381
382 if (pcszBad)
383 return setError(VBOX_E_FILE_ERROR,
384 tr("Error reading \"%s\": missing or invalid attribute '%s' in 'DiskSection' element, line %d"),
385 pcszPath,
386 pcszBad,
387 pelmDisk->getLineNumber());
388
389 // RTPrintf(" found disk: %s\n", d.strDiskId.c_str());
390 m->mapDisks[d.strDiskId] = d;
391 }
392
393 return S_OK;
394}
395
396/**
397 * Private helper method that handles network sections in the OVF XML.
398 * @param pcszPath Path spec of the XML file, for error messages.
399 * @param pSectionElem Section element for which this helper is getting called.
400 * @return
401 */
402HRESULT Appliance::HandleNetworkSection(const char *pcszPath,
403 const xml::Node *pSectionElem)
404{
405 // contains "Disk" child elements
406 xml::NodesLoop loopNetworks(*pSectionElem, "Network");
407 const xml::Node *pelmNetwork;
408 while ((pelmNetwork = loopNetworks.forAllNodes()))
409 {
410 Network n;
411 if (!(pelmNetwork->getAttributeValue("name", n.strNetworkName)))
412 return setError(VBOX_E_FILE_ERROR,
413 tr("Error reading \"%s\": missing 'name' attribute in 'Network', line %d"),
414 pcszPath,
415 pelmNetwork->getLineNumber());
416
417 m->mapNetworks[n.strNetworkName] = n;
418 }
419
420 return S_OK;
421}
422
423/**
424 * Private helper method that handles a "VirtualSystem" element in the OVF XML.
425 *
426 * @param pcszPath
427 * @param pContentElem
428 * @return
429 */
430HRESULT Appliance::HandleVirtualSystemContent(const char *pcszPath,
431 const xml::Node *pelmVirtualSystem)
432{
433 VirtualSystem d;
434
435 const xml::Node *pIdAttr = pelmVirtualSystem->findAttribute("id");
436 if (pIdAttr)
437 d.strName = pIdAttr->getValue();
438
439 xml::NodesLoop loop(*pelmVirtualSystem); // all child elements
440 const xml::Node *pelmThis;
441 while ((pelmThis = loop.forAllNodes()))
442 {
443 const char *pcszElemName = pelmThis->getName();
444 const xml::Node *pTypeAttr = pelmThis->findAttribute("type");
445 const char *pcszTypeAttr = (pTypeAttr) ? pTypeAttr->getValue() : "";
446
447 if (!strcmp(pcszElemName, "EulaSection"))
448 {
449 /* <EulaSection>
450 <Info ovf:msgid="6">License agreement for the Virtual System.</Info>
451 <License ovf:msgid="1">License terms can go in here.</License>
452 </EulaSection> */
453
454 const xml::Node *pelmInfo, *pelmLicense;
455 if ( ((pelmInfo = pelmThis->findChildElement("Info")))
456 && ((pelmLicense = pelmThis->findChildElement("License")))
457 )
458 {
459 d.strLicenceInfo = pelmInfo->getValue();
460 d.strLicenceText = pelmLicense->getValue();
461 }
462 }
463 else if ( (!strcmp(pcszElemName, "VirtualHardwareSection"))
464 || (!strcmp(pcszTypeAttr, "ovf:VirtualHardwareSection_Type"))
465 )
466 {
467 const xml::Node *pelmSystem, *pelmVirtualSystemType;
468 if ((pelmSystem = pelmThis->findChildElement("System")))
469 {
470 /* <System>
471 <vssd:Description>Description of the virtual hardware section.</vssd:Description>
472 <vssd:ElementName>vmware</vssd:ElementName>
473 <vssd:InstanceID>1</vssd:InstanceID>
474 <vssd:VirtualSystemIdentifier>MyLampService</vssd:VirtualSystemIdentifier>
475 <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType>
476 </System>*/
477 if ((pelmVirtualSystemType = pelmSystem->findChildElement("VirtualSystemType")))
478 d.strVirtualSystemType = pelmVirtualSystemType->getValue();
479 }
480
481 xml::NodesLoop loopVirtualHardwareItems(*pelmThis, "Item"); // all "Item" child elements
482 const xml::Node *pelmItem;
483 while ((pelmItem = loopVirtualHardwareItems.forAllNodes()))
484 {
485 VirtualHardwareItem i;
486
487 i.ulLineNumber = pelmItem->getLineNumber();
488
489 xml::NodesLoop loopItemChildren(*pelmItem); // all child elements
490 const xml::Node *pelmItemChild;
491 while ((pelmItemChild = loopItemChildren.forAllNodes()))
492 {
493 const char *pcszItemChildName = pelmItemChild->getName();
494 if (!strcmp(pcszItemChildName, "Description"))
495 i.strDescription = pelmItemChild->getValue();
496 else if (!strcmp(pcszItemChildName, "Caption"))
497 i.strCaption = pelmItemChild->getValue();
498 else if (!strcmp(pcszItemChildName, "ElementName"))
499 i.strElementName = pelmItemChild->getValue();
500 else if ( (!strcmp(pcszItemChildName, "InstanceID"))
501 || (!strcmp(pcszItemChildName, "InstanceId"))
502 )
503 pelmItemChild->copyValue(i.ulInstanceID);
504 else if (!strcmp(pcszItemChildName, "HostResource"))
505 i.strHostResource = pelmItemChild->getValue();
506 else if (!strcmp(pcszItemChildName, "ResourceType"))
507 {
508 int32_t iType; /** @todo how to fix correctly? (enum fun.) */
509 pelmItemChild->copyValue(iType);
510 i.resourceType = (OVFResourceType_T)iType;
511 }
512 else if (!strcmp(pcszItemChildName, "OtherResourceType"))
513 i.strOtherResourceType = pelmItemChild->getValue();
514 else if (!strcmp(pcszItemChildName, "ResourceSubType"))
515 i.strResourceSubType = pelmItemChild->getValue();
516 else if (!strcmp(pcszItemChildName, "AutomaticAllocation"))
517 i.fAutomaticAllocation = (!strcmp(pelmItemChild->getValue(), "true")) ? true : false;
518 else if (!strcmp(pcszItemChildName, "AutomaticDeallocation"))
519 i.fAutomaticDeallocation = (!strcmp(pelmItemChild->getValue(), "true")) ? true : false;
520 else if (!strcmp(pcszItemChildName, "Parent"))
521 pelmItemChild->copyValue(i.ulParent);
522 else if (!strcmp(pcszItemChildName, "Connection"))
523 i.strConnection = pelmItemChild->getValue();
524 else if (!strcmp(pcszItemChildName, "Address"))
525 i.strAddress = pelmItemChild->getValue();
526 else if (!strcmp(pcszItemChildName, "AddressOnParent"))
527 i.strAddressOnParent = pelmItemChild->getValue();
528 else if (!strcmp(pcszItemChildName, "AllocationUnits"))
529 i.strAllocationUnits = pelmItemChild->getValue();
530 else if (!strcmp(pcszItemChildName, "VirtualQuantity"))
531 pelmItemChild->copyValue(i.ullVirtualQuantity);
532 else if (!strcmp(pcszItemChildName, "Reservation"))
533 pelmItemChild->copyValue(i.ullReservation);
534 else if (!strcmp(pcszItemChildName, "Limit"))
535 pelmItemChild->copyValue(i.ullLimit);
536 else if (!strcmp(pcszItemChildName, "Weight"))
537 pelmItemChild->copyValue(i.ullWeight);
538 else if (!strcmp(pcszItemChildName, "ConsumerVisibility"))
539 i.strConsumerVisibility = pelmItemChild->getValue();
540 else if (!strcmp(pcszItemChildName, "MappingBehavior"))
541 i.strMappingBehavior = pelmItemChild->getValue();
542 else if (!strcmp(pcszItemChildName, "PoolID"))
543 i.strPoolID = pelmItemChild->getValue();
544 else if (!strcmp(pcszItemChildName, "BusNumber"))
545 pelmItemChild->copyValue(i.ulBusNumber);
546 else
547 return setError(VBOX_E_FILE_ERROR,
548 tr("Error reading \"%s\": unknown element \"%s\" under Item element, line %d"),
549 pcszPath,
550 pcszItemChildName,
551 i.ulLineNumber);
552 }
553
554 // store!
555 d.mapHardwareItems[i.ulInstanceID] = i;
556 }
557
558 HardwareItemsMap::const_iterator itH;
559
560 for (itH = d.mapHardwareItems.begin();
561 itH != d.mapHardwareItems.end();
562 ++itH)
563 {
564 const VirtualHardwareItem &i = itH->second;
565
566 // do some analysis
567 switch (i.resourceType)
568 {
569 case OVFResourceType_Processor: // 3
570 /* <rasd:Caption>1 virtual CPU</rasd:Caption>
571 <rasd:Description>Number of virtual CPUs</rasd:Description>
572 <rasd:ElementName>virtual CPU</rasd:ElementName>
573 <rasd:InstanceID>1</rasd:InstanceID>
574 <rasd:ResourceType>3</rasd:ResourceType>
575 <rasd:VirtualQuantity>1</rasd:VirtualQuantity>*/
576 if (i.ullVirtualQuantity < UINT16_MAX)
577 d.cCPUs = (uint16_t)i.ullVirtualQuantity;
578 else
579 return setError(VBOX_E_FILE_ERROR,
580 tr("Error reading \"%s\": CPU count %RI64 is larger than %d, line %d"),
581 pcszPath,
582 i.ullVirtualQuantity,
583 UINT16_MAX,
584 i.ulLineNumber);
585 break;
586
587 case OVFResourceType_Memory: // 4
588 if ( (i.strAllocationUnits == "MegaBytes") // found in OVF created by OVF toolkit
589 || (i.strAllocationUnits == "MB") // found in MS docs
590 || (i.strAllocationUnits == "byte * 2^20") // suggested by OVF spec DSP0243 page 21
591 )
592 d.ullMemorySize = i.ullVirtualQuantity * 1024 * 1024;
593 else
594 return setError(VBOX_E_FILE_ERROR,
595 tr("Error reading \"%s\": Invalid allocation unit \"%s\" specified with memory size item, line %d"),
596 pcszPath,
597 i.strAllocationUnits.c_str(),
598 i.ulLineNumber);
599 break;
600
601 case OVFResourceType_IdeController: // 5 IdeController
602 {
603 /* <Item>
604 <rasd:Caption>ideController0</rasd:Caption>
605 <rasd:Description>IDE Controller</rasd:Description>
606 <rasd:InstanceId>5</rasd:InstanceId>
607 <rasd:ResourceType>5</rasd:ResourceType>
608 <rasd:Address>0</rasd:Address>
609 <rasd:BusNumber>0</rasd:BusNumber>
610 </Item> */
611 HardDiskController hdc;
612 hdc.idController = i.ulInstanceID;
613 hdc.controllerSystem = IDE;
614 hdc.strAddress = i.strAddress;
615 hdc.ulBusNumber = i.ulBusNumber;
616
617 d.mapControllers[i.ulInstanceID] = hdc;
618 }
619
620 case OVFResourceType_ParallelScsiHba: // 6 SCSI controller
621 {
622 /* <Item>
623 <rasd:Caption>SCSI Controller 0 - LSI Logic</rasd:Caption>
624 <rasd:Description>SCI Controller</rasd:Description>
625 <rasd:ElementName>SCSI controller</rasd:ElementName>
626 <rasd:InstanceID>4</rasd:InstanceID>
627 <rasd:ResourceSubType>LsiLogic</rasd:ResourceSubType>
628 <rasd:ResourceType>6</rasd:ResourceType>
629 </Item> */
630 HardDiskController hdc;
631 hdc.idController = i.ulInstanceID;
632 hdc.controllerSystem = SCSI;
633 hdc.strControllerType = i.strResourceSubType;
634
635 d.mapControllers[i.ulInstanceID] = hdc;
636 }
637 break;
638
639 case OVFResourceType_EthernetAdapter: // 10
640 {
641 /* <Item>
642 <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
643 <rasd:Caption>Ethernet adapter on 'VM Network'</rasd:Caption>
644 <rasd:Connection>VM Network</rasd:Connection>
645 <rasd:Description>VM Network?</rasd:Description>
646 <rasd:ElementName>Ethernet adapter</rasd:ElementName>
647 <rasd:InstanceID>3</rasd:InstanceID>
648 <rasd:ResourceType>10</rasd:ResourceType>
649 </Item>
650
651 OVF spec DSP 0243 page 21:
652 "For an Ethernet adapter, this specifies the abstract network connection name
653 for the virtual machine. All Ethernet adapters that specify the same abstract
654 network connection name within an OVF package shall be deployed on the same
655 network. The abstract network connection name shall be listed in the NetworkSection
656 at the outermost envelope level." */
657
658 // make sure we have a matching NetworkSection/Network
659 NetworksMap::iterator it = m->mapNetworks.find(i.strConnection);
660 if (it == m->mapNetworks.end())
661 return setError(VBOX_E_FILE_ERROR,
662 tr("Error reading \"%s\": Invalid connection \"%s\"; cannot find matching NetworkSection/Network element, line %d"),
663 pcszPath,
664 i.strConnection.c_str(),
665 i.ulLineNumber);
666
667 d.llNetworkNames.push_back(i.strConnection);
668 }
669 break;
670
671 case OVFResourceType_HardDisk: // 17
672 {
673 /* <Item>
674 <rasd:Caption>Harddisk 1</rasd:Caption>
675 <rasd:Description>HD</rasd:Description>
676 <rasd:ElementName>Hard Disk</rasd:ElementName>
677 <rasd:HostResource>ovf://disk/lamp</rasd:HostResource>
678 <rasd:InstanceID>5</rasd:InstanceID>
679 <rasd:Parent>4</rasd:Parent>
680 <rasd:ResourceType>17</rasd:ResourceType>
681 </Item> */
682
683 // look up the hard disk controller element whose InstanceID equals our Parent;
684 // this is how the connection is specified in OVF
685 ControllersMap::const_iterator it = d.mapControllers.find(i.ulParent);
686 if (it == d.mapControllers.end())
687 return setError(VBOX_E_FILE_ERROR,
688 tr("Error reading \"%s\": Hard disk item with instance ID %d specifies invalid parent %d, line %d"),
689 pcszPath,
690 i.ulInstanceID,
691 i.ulParent,
692 i.ulLineNumber);
693 const HardDiskController &hdc = it->second;
694
695 VirtualDisk vd;
696 vd.idController = i.ulParent;
697 bool fFound = false;
698 // ovf://disk/lamp
699 // 12345678901234
700 if (i.strHostResource.substr(0, 11) == "ovf://disk/")
701 vd.strDiskId = i.strHostResource.substr(11);
702 else if (i.strHostResource.substr(0, 6) == "/disk/")
703 vd.strDiskId = i.strHostResource.substr(6);
704
705 if ( !(vd.strDiskId.length())
706 || (m->mapDisks.find(vd.strDiskId) == m->mapDisks.end())
707 )
708 return setError(VBOX_E_FILE_ERROR,
709 tr("Error reading \"%s\": Hard disk item with instance ID %d specifies invalid host resource \"%s\", line %d"),
710 pcszPath,
711 i.ulInstanceID,
712 i.strHostResource.c_str(),
713 i.ulLineNumber);
714
715 d.llVirtualDisks.push_back(vd);
716 }
717 break;
718 }
719 }
720 }
721 else if ( (!strcmp(pcszElemName, "OperatingSystemSection"))
722 || (!strcmp(pcszTypeAttr, "ovf:OperatingSystemSection_Type"))
723 )
724 {
725 uint64_t cimos64;
726 if (!(pelmThis->getAttributeValue("id", cimos64)))
727 return setError(VBOX_E_FILE_ERROR,
728 tr("Error reading \"%s\": missing or invalid 'ovf:id' attribute in operating system section element, line %d"),
729 pcszPath,
730 pelmThis->getLineNumber());
731
732 d.cimos = (CIMOSType_T)cimos64;
733 }
734 }
735
736 // now create the virtual system
737 m->llVirtualSystems.push_back(d);
738
739 return S_OK;
740}
741
742// IAppliance public methods
743////////////////////////////////////////////////////////////////////////////////
744
745/**
746 * Appliance initializer.
747 *
748 * This loads the given appliance.
749 * @param
750 * @return
751 */
752
753HRESULT Appliance::init(VirtualBox *aVirtualBox, IN_BSTR &path)
754{
755 HRESULT rc;
756
757 /* Enclose the state transition NotReady->InInit->Ready */
758 AutoInitSpan autoInitSpan(this);
759 AssertReturn(autoInitSpan.isOk(), E_FAIL);
760
761 /* Weakly reference to a VirtualBox object */
762 unconst(mVirtualBox) = aVirtualBox;
763
764 // initialize data
765 m = new Data;
766 m->bstrPath = path;
767
768 // see if we can handle this file; for now we insist it has an ".ovf" extension
769 Utf8Str utf8Path(path);
770 const char *pcszLastDot = strrchr(utf8Path, '.');
771 if ( (!pcszLastDot)
772 || ( strcmp(pcszLastDot, ".ovf")
773 && strcmp(pcszLastDot, ".OVF")
774 )
775 )
776 return setError(VBOX_E_FILE_ERROR,
777 tr("Appliance file must have .ovf extension"));
778
779 try
780 {
781 xml::XmlFileParser parser;
782 xml::Document doc;
783 parser.read(utf8Path.raw(),
784 doc);
785
786 const xml::Node *pRootElem = doc.getRootElement();
787 if (strcmp(pRootElem->getName(), "Envelope"))
788 return setError(VBOX_E_FILE_ERROR,
789 tr("Root element in OVF file must be \"Envelope\"."));
790
791 // OVF has the following rough layout:
792 /*
793 -- <References> .... files referenced from other parts of the file, such as VMDK images
794 -- Metadata, comprised of several section commands
795 -- virtual machines, either a single <VirtualSystem>, or a <VirtualSystemCollection>
796 -- optionally <Strings> for localization
797 */
798
799 // get all "File" child elements of "References" section so we can look up files easily;
800 // first find the "References" sections so we can look up files
801 xml::NodesList listFileElements; // receives all /Envelope/References/File nodes
802 const xml::Node *pReferencesElem;
803 if ((pReferencesElem = pRootElem->findChildElement("References")))
804 pReferencesElem->getChildElements(listFileElements, "File");
805
806 // now go though the sections
807 if (!(SUCCEEDED(rc = LoopThruSections(utf8Path.raw(), pReferencesElem, pRootElem))))
808 return rc;
809 }
810 catch(xml::Error &x)
811 {
812 return setError(VBOX_E_FILE_ERROR,
813 x.what());
814 }
815
816 /* Confirm a successful initialization */
817 autoInitSpan.setSucceeded();
818
819 return S_OK;
820}
821
822void Appliance::uninit()
823{
824 delete m;
825 m = NULL;
826}
827
828STDMETHODIMP Appliance::COMGETTER(Path)(BSTR *aPath)
829{
830 if (!aPath)
831 return E_POINTER;
832
833 AutoCaller autoCaller(this);
834 CheckComRCReturnRC(autoCaller.rc());
835
836 AutoReadLock alock(this);
837
838 m->bstrPath.cloneTo(aPath);
839
840 return S_OK;
841}
842
843STDMETHODIMP Appliance::COMGETTER(Disks)(ComSafeArrayOut(BSTR, aDisks))
844{
845 CheckComArgOutSafeArrayPointerValid(aDisks);
846
847 AutoCaller autoCaller(this);
848 CheckComRCReturnRC(autoCaller.rc());
849
850 AutoReadLock alock(this);
851
852 size_t c = m->mapDisks.size();
853 com::SafeArray<BSTR> sfaDisks(c);
854
855 DiskImagesMap::const_iterator it;
856 size_t i = 0;
857 for (it = m->mapDisks.begin();
858 it != m->mapDisks.end();
859 ++it, ++i)
860 {
861 // create a string representing this disk
862 const DiskImage &d = it->second;
863 char *psz = NULL;
864 RTStrAPrintf(&psz,
865 "%s\t"
866 "%RI64\t"
867 "%RI64\t"
868 "%s\t"
869 "%s\t"
870 "%RI64\t"
871 "%RI64\t"
872 "%s",
873 d.strDiskId.c_str(),
874 d.iCapacity,
875 d.iPopulatedSize,
876 d.strFormat.c_str(),
877 d.strHref.c_str(),
878 d.iSize,
879 d.iChunkSize,
880 d.strCompression.c_str());
881 Utf8Str utf(psz);
882 Bstr bstr(utf);
883 // push to safearray
884 bstr.cloneTo(&sfaDisks[i]);
885 RTStrFree(psz);
886 }
887
888 sfaDisks.detachTo(ComSafeArrayOutArg(aDisks));
889
890 return S_OK;
891}
892
893STDMETHODIMP Appliance::COMGETTER(VirtualSystemDescriptions)(ComSafeArrayOut(IVirtualSystemDescription*, aVirtualSystemDescriptions))
894{
895 CheckComArgOutSafeArrayPointerValid(aVirtualSystemDescriptions);
896
897 AutoCaller autoCaller(this);
898 CheckComRCReturnRC(autoCaller.rc());
899
900 AutoReadLock alock(this);
901
902 SafeIfaceArray<IVirtualSystemDescription> sfaVSD(m->virtualSystemDescriptions);
903 sfaVSD.detachTo(ComSafeArrayOutArg(aVirtualSystemDescriptions));
904
905 return S_OK;
906}
907
908STDMETHODIMP Appliance::Interpret()
909{
910 // @todo:
911 // - Locking
912 // - COM error handling
913 // - don't use COM methods but the methods directly (faster, but needs appropriate locking of that objects itself (s. HardDisk2))
914 // - Appropriate handle errors like not supported file formats
915
916 HRESULT rc = S_OK;
917
918 /* Clear any previous virtual system descriptions */
919 // @todo: have the entries deleted also?
920 m->virtualSystemDescriptions.clear();
921
922 /* We need the default path for storing disk images */
923 ISystemProperties *systemProps = NULL;
924 rc = mVirtualBox->COMGETTER(SystemProperties)(&systemProps);
925 ComAssertComRCThrowRC(rc);
926 BSTR defaultHardDiskLocation;
927 rc = systemProps->COMGETTER(DefaultHardDiskFolder)(&defaultHardDiskLocation);
928 ComAssertComRCThrowRC(rc);
929
930 list<VirtualSystem>::const_iterator it;
931 /* Iterate through all appliances */
932 for (it = m->llVirtualSystems.begin();
933 it != m->llVirtualSystems.end();
934 ++it)
935 {
936 const VirtualSystem &vs = *it;
937 ComObjPtr <VirtualSystemDescription> vsd;
938 vsd.createObject();
939 rc = vsd->init();
940 ComAssertComRCThrowRC(rc);
941
942 string osTypeVBox = SchemaDefs_OSTypeId_Other;
943 /* Guest OS type */
944 switch (vs.cimos)
945 {
946 case CIMOSType_CIMOS_Unknown: // 0 - Unknown
947 osTypeVBox = SchemaDefs_OSTypeId_Other;
948 break;
949
950 case CIMOSType_CIMOS_OS2: // 12 - OS/2
951 osTypeVBox = SchemaDefs_OSTypeId_OS2;
952 break;
953
954 case CIMOSType_CIMOS_MSDOS: // 14 - MSDOS
955 osTypeVBox = SchemaDefs_OSTypeId_DOS;
956 break;
957
958 case CIMOSType_CIMOS_WIN3x: // 15 - WIN3x
959 osTypeVBox = SchemaDefs_OSTypeId_Windows31;
960 break;
961
962 case CIMOSType_CIMOS_WIN95: // 16 - WIN95
963 osTypeVBox = SchemaDefs_OSTypeId_Windows95;
964 break;
965
966 case CIMOSType_CIMOS_WIN98: // 17 - WIN98
967 osTypeVBox = SchemaDefs_OSTypeId_Windows98;
968 break;
969
970 case CIMOSType_CIMOS_WINNT: // 18 - WINNT
971 osTypeVBox = SchemaDefs_OSTypeId_WindowsNT4;
972 break;
973
974 case CIMOSType_CIMOS_NetWare: // 21 - NetWare
975 case CIMOSType_CIMOS_NovellOES: // 86 - Novell OES
976 osTypeVBox = SchemaDefs_OSTypeId_Netware;
977 break;
978
979 case CIMOSType_CIMOS_Solaris: // 29 - Solaris
980 case CIMOSType_CIMOS_SunOS: // 30 - SunOS
981 osTypeVBox = SchemaDefs_OSTypeId_Solaris;
982 break;
983
984 case CIMOSType_CIMOS_FreeBSD: // 42 - FreeBSD
985 osTypeVBox = SchemaDefs_OSTypeId_FreeBSD;
986 break;
987
988 case CIMOSType_CIMOS_NetBSD: // 43 - NetBSD
989 osTypeVBox = SchemaDefs_OSTypeId_NetBSD;
990 break;
991
992 case CIMOSType_CIMOS_QNX: // 48 - QNX
993 osTypeVBox = SchemaDefs_OSTypeId_QNX;
994 break;
995
996 case CIMOSType_CIMOS_Windows2000: // 58 - Windows 2000
997 osTypeVBox = SchemaDefs_OSTypeId_Windows2000;
998 break;
999
1000 case CIMOSType_CIMOS_WindowsMe: // 63 - Windows (R) Me
1001 osTypeVBox = SchemaDefs_OSTypeId_WindowsMe;
1002 break;
1003
1004 case CIMOSType_CIMOS_OpenBSD: // 65 - OpenBSD
1005 osTypeVBox = SchemaDefs_OSTypeId_OpenBSD;
1006 break;
1007
1008 case CIMOSType_CIMOS_WindowsXP: // 67 - Windows XP
1009 case CIMOSType_CIMOS_WindowsXPEmbedded: // 72 - Windows XP Embedded
1010 case CIMOSType_CIMOS_WindowsEmbeddedforPointofService: // 75 - Windows Embedded for Point of Service
1011 osTypeVBox = SchemaDefs_OSTypeId_WindowsXP;
1012 break;
1013
1014 case CIMOSType_CIMOS_MicrosoftWindowsServer2003: // 69 - Microsoft Windows Server 2003
1015 osTypeVBox = SchemaDefs_OSTypeId_Windows2003;
1016 break;
1017
1018 case CIMOSType_CIMOS_MicrosoftWindowsServer2003_64: // 70 - Microsoft Windows Server 2003 64-Bit
1019 osTypeVBox = SchemaDefs_OSTypeId_Windows2003_64;
1020 break;
1021
1022 case CIMOSType_CIMOS_WindowsXP_64: // 71 - Windows XP 64-Bit
1023 osTypeVBox = SchemaDefs_OSTypeId_WindowsXP_64;
1024 break;
1025
1026 case CIMOSType_CIMOS_WindowsVista: // 73 - Windows Vista
1027 osTypeVBox = SchemaDefs_OSTypeId_WindowsVista;
1028 break;
1029
1030 case CIMOSType_CIMOS_WindowsVista_64: // 74 - Windows Vista 64-Bit
1031 osTypeVBox = SchemaDefs_OSTypeId_WindowsVista_64;
1032 break;
1033
1034 case CIMOSType_CIMOS_MicrosoftWindowsServer2008: // 76 - Microsoft Windows Server 2008
1035 osTypeVBox = SchemaDefs_OSTypeId_Windows2008;
1036 break;
1037
1038 case CIMOSType_CIMOS_MicrosoftWindowsServer2008_64: // 77 - Microsoft Windows Server 2008 64-Bit
1039 osTypeVBox = SchemaDefs_OSTypeId_Windows2008_64;
1040 break;
1041
1042 case CIMOSType_CIMOS_FreeBSD_64: // 78 - FreeBSD 64-Bit
1043 osTypeVBox = SchemaDefs_OSTypeId_FreeBSD_64;
1044 break;
1045
1046 case CIMOSType_CIMOS_RedHatEnterpriseLinux: // 79 - RedHat Enterprise Linux
1047 osTypeVBox = SchemaDefs_OSTypeId_RedHat;
1048 break;
1049
1050 case CIMOSType_CIMOS_RedHatEnterpriseLinux_64: // 80 - RedHat Enterprise Linux 64-Bit
1051 osTypeVBox = SchemaDefs_OSTypeId_RedHat_64;
1052 break;
1053
1054 case CIMOSType_CIMOS_Solaris_64: // 81 - Solaris 64-Bit
1055 osTypeVBox = SchemaDefs_OSTypeId_Solaris_64;
1056 break;
1057
1058 case CIMOSType_CIMOS_SUSE: // 82 - SUSE
1059 case CIMOSType_CIMOS_SLES: // 84 - SLES
1060 case CIMOSType_CIMOS_NovellLinuxDesktop: // 87 - Novell Linux Desktop
1061 osTypeVBox = SchemaDefs_OSTypeId_OpenSUSE;
1062 break;
1063
1064 case CIMOSType_CIMOS_SUSE_64: // 83 - SUSE 64-Bit
1065 case CIMOSType_CIMOS_SLES_64: // 85 - SLES 64-Bit
1066 osTypeVBox = SchemaDefs_OSTypeId_OpenSUSE_64;
1067 break;
1068
1069 case CIMOSType_CIMOS_LINUX: // 36 - LINUX
1070 case CIMOSType_CIMOS_SunJavaDesktopSystem: // 88 - Sun Java Desktop System
1071 case CIMOSType_CIMOS_TurboLinux: // 91 - TurboLinux
1072 osTypeVBox = SchemaDefs_OSTypeId_Linux;
1073 break;
1074
1075 // case CIMOSType_CIMOS_TurboLinux_64: // 92 - TurboLinux 64-Bit
1076 // case CIMOSType_CIMOS_Linux_64: // 101 - Linux 64-Bit
1077 // osTypeVBox = VBOXOSTYPE_Linux_x64;
1078 // break;
1079
1080 case CIMOSType_CIMOS_Mandriva: // 89 - Mandriva
1081 osTypeVBox = SchemaDefs_OSTypeId_Mandriva;
1082 break;
1083
1084 case CIMOSType_CIMOS_Mandriva_64: // 90 - Mandriva 64-Bit
1085 osTypeVBox = SchemaDefs_OSTypeId_Mandriva_64;
1086 break;
1087
1088 case CIMOSType_CIMOS_Ubuntu: // 93 - Ubuntu
1089 osTypeVBox = SchemaDefs_OSTypeId_Ubuntu;
1090 break;
1091
1092 case CIMOSType_CIMOS_Ubuntu_64: // 94 - Ubuntu 64-Bit
1093 osTypeVBox = SchemaDefs_OSTypeId_Ubuntu_64;
1094 break;
1095
1096 case CIMOSType_CIMOS_Debian: // 95 - Debian
1097 osTypeVBox = SchemaDefs_OSTypeId_Debian;
1098 break;
1099
1100 case CIMOSType_CIMOS_Debian_64: // 96 - Debian 64-Bit
1101 osTypeVBox = SchemaDefs_OSTypeId_Debian_64;
1102 break;
1103
1104 case CIMOSType_CIMOS_Linux_2_4_x: // 97 - Linux 2.4.x
1105 osTypeVBox = SchemaDefs_OSTypeId_Linux24;
1106 break;
1107
1108 case CIMOSType_CIMOS_Linux_2_4_x_64: // 98 - Linux 2.4.x 64-Bit
1109 osTypeVBox = SchemaDefs_OSTypeId_Linux24_64;
1110 break;
1111
1112 case CIMOSType_CIMOS_Linux_2_6_x: // 99 - Linux 2.6.x
1113 osTypeVBox = SchemaDefs_OSTypeId_Linux26;
1114 break;
1115
1116 case CIMOSType_CIMOS_Linux_2_6_x_64: // 100 - Linux 2.6.x 64-Bit
1117 osTypeVBox = SchemaDefs_OSTypeId_Linux26_64;
1118 break;
1119 default:
1120 {
1121 /* If we are here we have no clue what OS this should be. Set to
1122 * other type as default. */
1123 osTypeVBox = SchemaDefs_OSTypeId_Other;
1124 }
1125 }
1126 vsd->addEntry(VirtualSystemDescriptionType_OS, 0, toString<ULONG>(vs.cimos), osTypeVBox);
1127
1128 /* VM name */
1129 /* If the there isn't any name specified create a default one out of
1130 * the OS type */
1131 string nameVBox = vs.strName;
1132 if (nameVBox == "")
1133 nameVBox = osTypeVBox;
1134 searchUniqueVMName(nameVBox);
1135 vsd->addEntry(VirtualSystemDescriptionType_Name, 0, vs.strName, nameVBox);
1136
1137 /* Now that we know the base system get our internal defaults based on that. */
1138 IGuestOSType *osType = NULL;
1139 rc = mVirtualBox->GetGuestOSType(Bstr(Utf8Str(osTypeVBox.c_str())), &osType);
1140 ComAssertComRCThrowRC(rc);
1141
1142 /* CPU count */
1143 /* @todo: check min/max requirements of VBox (SchemaDefs::Min/MaxCPUCount) */
1144 ULONG cpuCountVBox = vs.cCPUs;
1145 if (vs.cCPUs == 0)
1146 cpuCountVBox = 1;
1147 vsd->addEntry(VirtualSystemDescriptionType_CPU, 0, toString<ULONG>(vs.cCPUs), toString<ULONG>(cpuCountVBox));
1148
1149 /* RAM */
1150 /* @todo: check min/max requirements of VBox (SchemaDefs::Min/MaxGuestRAM) */
1151 uint64_t ullMemSizeVBox = vs.ullMemorySize; /** @todo r=bird/MSC: this will overflow at 4GB, use 64-bit type. */
1152 if (vs.ullMemorySize == 0)
1153 {
1154 /* If the RAM of the OVF is zero, use our predefined values */
1155 ULONG memSizeVBox2;
1156 rc = osType->COMGETTER(RecommendedRAM)(&memSizeVBox2);
1157 ComAssertComRCThrowRC(rc);
1158 /* VBox stores that in MByte */
1159 ullMemSizeVBox = memSizeVBox2 * _1M;
1160 }
1161 vsd->addEntry(VirtualSystemDescriptionType_Memory, 0, toString<uint64_t>(vs.ullMemorySize), toString<uint64_t>(ullMemSizeVBox));
1162
1163 /* Hard disk Controller */
1164 ControllersMap::const_iterator hdcIt;
1165 /* Iterate through all hard disk controllers */
1166 for (hdcIt = vs.mapControllers.begin();
1167 hdcIt != vs.mapControllers.end();
1168 ++hdcIt)
1169 {
1170 HardDiskController hdc = hdcIt->second;
1171 switch (hdc.controllerSystem)
1172 {
1173 case IDE:
1174 {
1175 // @todo: figure out the IDE types
1176 /* Use PIIX4 as default */
1177 IDEControllerType_T hdcController = IDEControllerType_PIIX4;
1178 if (!RTStrICmp(hdc.strControllerType.c_str(), "PIIX3"))
1179 hdcController = IDEControllerType_PIIX3;
1180 else if (!RTStrICmp(hdc.strControllerType.c_str(), "PIIX4"))
1181 hdcController = IDEControllerType_PIIX4;
1182 vsd->addEntry(VirtualSystemDescriptionType_HarddiskControllerIDE, hdc.idController, hdc.strControllerType, toString<ULONG>(hdcController));
1183 break;
1184 }
1185 case SATA:
1186 {
1187 // @todo: figure out the SATA types
1188 /* We only support a plain AHCI controller, so use them always */
1189 vsd->addEntry(VirtualSystemDescriptionType_HarddiskControllerSATA, hdc.idController, hdc.strControllerType, "AHCI");
1190 break;
1191 }
1192 case SCSI:
1193 {
1194 string hdcController = "LsiLogic";
1195 // @todo: figure out the SCSI types
1196 if (!RTStrICmp(hdc.strControllerType.c_str(), "LsiLogic"))
1197 hdcController = "LsiLogic";
1198 else if (!RTStrICmp(hdc.strControllerType.c_str(), "BusLogic"))
1199 hdcController = "BusLogic";
1200 vsd->addEntry(VirtualSystemDescriptionType_HarddiskControllerSCSI, hdc.idController, hdc.strControllerType, hdcController);
1201 break;
1202 }
1203 }
1204 }
1205
1206 /* Hard disks */
1207 if (vs.llVirtualDisks.size() > 0)
1208 {
1209 // @todo:
1210 // - strHref could be empty (construct a new default file name)
1211 // - check that the filename is unique to vbox in any case
1212 list<VirtualDisk>::const_iterator hdIt;
1213 /* Iterate through all hard disks ()*/
1214 for (hdIt = vs.llVirtualDisks.begin();
1215 hdIt != vs.llVirtualDisks.end();
1216 ++hdIt)
1217 {
1218 VirtualDisk hd = *hdIt;
1219 /* Get the associated disk image */
1220 DiskImage di = m->mapDisks [hd.strDiskId];
1221 /* We have to check if we support this format */
1222 bool fSupported = false;
1223 // @todo:
1224 // - figure out all possible vmdk formats we also support
1225 // - figure out if there is a url specifier for vhd already
1226 // - we need a url specifier for the vdi format
1227 if (!RTStrICmp(di.strFormat.c_str(), "http://www.vmware.com/specifications/vmdk.html#sparse"))
1228 fSupported = true;
1229 /* enable compressed formats for the first tests also */
1230 else if (!RTStrICmp(di.strFormat.c_str(), "http://www.vmware.com/specifications/vmdk.html#compressed"))
1231 fSupported = true;
1232 if (fSupported)
1233 {
1234 /* Construct the path */
1235 string path = Utf8StrFmt("%ls%c%s", defaultHardDiskLocation, RTPATH_DELIMITER, di.strHref.c_str()).raw();
1236 vsd->addEntry(VirtualSystemDescriptionType_Harddisk, hd.idController, di.strHref, path);
1237 }
1238 }
1239 }
1240
1241 /* Network Controller */
1242 // @todo: is there no hardware specified in the OVF-Format?
1243 if (vs.llNetworkNames.size() > 0)
1244 {
1245 /* Get the default network adapter type for the selected guest OS */
1246 NetworkAdapterType_T nwAdapterVBox = NetworkAdapterType_Am79C970A;
1247 rc = osType->COMGETTER(AdapterType)(&nwAdapterVBox);
1248 ComAssertComRCThrowRC(rc);
1249 list<string>::const_iterator nwIt;
1250 /* Iterate through all abstract networks. We support 8 network
1251 * adapters at the maximum. (@todo: warn if it are more!) */
1252 size_t a = 0;
1253 for (nwIt = vs.llNetworkNames.begin();
1254 nwIt != vs.llNetworkNames.end() && a < SchemaDefs::NetworkAdapterCount;
1255 ++nwIt, ++a)
1256 {
1257 // string nwController = *nwIt; // @todo: not used yet
1258 vsd->addEntry(VirtualSystemDescriptionType_NetworkAdapter, 0, "", toString<ULONG>(nwAdapterVBox));
1259 }
1260 }
1261 m->virtualSystemDescriptions.push_back(vsd);
1262 }
1263
1264 return S_OK;
1265}
1266
1267STDMETHODIMP Appliance::ImportAppliance()
1268{
1269 HRESULT rc = S_OK;
1270
1271 list<VirtualSystem>::const_iterator it;
1272 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it1;
1273 /* Iterate through all appliances */
1274 size_t i = 0;
1275 for (it = m->llVirtualSystems.begin(),
1276 it1 = m->virtualSystemDescriptions.begin();
1277 it != m->llVirtualSystems.end();
1278 ++it, ++it1, ++i)
1279 {
1280 const VirtualSystem &vs = *it;
1281 ComObjPtr<VirtualSystemDescription> vsd = (*it1);
1282
1283 /* Guest OS type */
1284 list<VirtualSystemDescriptionEntry> vsdeOS = vsd->findByType(VirtualSystemDescriptionType_OS);
1285 Assert(vsdeOS.size() == 1);
1286 string osTypeVBox = vsdeOS.front().strFinalValue;
1287
1288 /* Now that we know the base system get our internal defaults based on that. */
1289 IGuestOSType *osType = NULL;
1290 rc = mVirtualBox->GetGuestOSType(Bstr(Utf8Str(osTypeVBox.c_str())), &osType);
1291 ComAssertComRCThrowRC(rc);
1292
1293 /* Create the machine */
1294 /* First get the name */
1295 list<VirtualSystemDescriptionEntry> vsdeName = vsd->findByType(VirtualSystemDescriptionType_Name);
1296 Assert(vsdeName.size() == 1);
1297 string nameVBox = vsdeName.front().strFinalValue;
1298 IMachine *newMachine = NULL;
1299 rc = mVirtualBox->CreateMachine(Bstr(nameVBox.c_str()), Bstr(osTypeVBox.c_str()),
1300 Bstr(), Guid(),
1301 &newMachine);
1302 ComAssertComRCThrowRC(rc);
1303
1304 /* CPU count (ignored for now) */
1305 /* @todo: check min/max requirements of VBox (SchemaDefs::Min/MaxCPUCount) */
1306 // list<VirtualSystemDescriptionEntry> vsdeCPU = vsd->findByType (VirtualSystemDescriptionType_CPU);
1307
1308 /* RAM */
1309 /* @todo: check min/max requirements of VBox (SchemaDefs::Min/MaxGuestRAM) */
1310 list<VirtualSystemDescriptionEntry> vsdeRAM = vsd->findByType(VirtualSystemDescriptionType_Memory);
1311 Assert(vsdeRAM.size() == 1);
1312 string memoryVBox = vsdeRAM.front().strFinalValue;
1313 uint64_t tt = RTStrToUInt64(memoryVBox.c_str()) / _1M;
1314
1315 rc = newMachine->COMSETTER(MemorySize)(tt);
1316 ComAssertComRCThrowRC(rc);
1317
1318 /* VRAM */
1319 /* Get the recommended VRAM for this guest OS type */
1320 /* @todo: check min/max requirements of VBox (SchemaDefs::Min/MaxGuestVRAM) */
1321 ULONG vramVBox;
1322 rc = osType->COMGETTER(RecommendedVRAM)(&vramVBox);
1323 ComAssertComRCThrowRC(rc);
1324 /* Set the VRAM */
1325 rc = newMachine->COMSETTER(VRAMSize)(vramVBox);
1326 ComAssertComRCThrowRC(rc);
1327
1328 /* Change the network adapters */
1329 list<VirtualSystemDescriptionEntry> vsdeNW = vsd->findByType(VirtualSystemDescriptionType_NetworkAdapter);
1330 if (vsdeNW.size() == 0)
1331 {
1332 /* No network adapters, so we have to disable our default one */
1333 INetworkAdapter *nwVBox = NULL;
1334 rc = newMachine->GetNetworkAdapter(0, &nwVBox);
1335 ComAssertComRCThrowRC(rc);
1336 rc = nwVBox->COMSETTER(Enabled)(false);
1337 ComAssertComRCThrowRC(rc);
1338 }
1339 else
1340 {
1341 list<VirtualSystemDescriptionEntry>::const_iterator nwIt;
1342 /* Iterate through all network cards. We support 8 network adapters
1343 * at the maximum. (@todo: warn if it are more!) */
1344 size_t a = 0;
1345 for (nwIt = vsdeNW.begin();
1346 (nwIt != vsdeNW.end() && a < SchemaDefs::NetworkAdapterCount);
1347 ++nwIt, ++a)
1348 {
1349 string nwTypeVBox = nwIt->strFinalValue;
1350 uint32_t tt1 = RTStrToUInt32(nwTypeVBox.c_str());
1351 INetworkAdapter *nwVBox = NULL;
1352 rc = newMachine->GetNetworkAdapter((ULONG)a, &nwVBox);
1353 ComAssertComRCThrowRC(rc);
1354 /* Enable the network card & set the adapter type */
1355 /* NAT is set as default */
1356 rc = nwVBox->COMSETTER(Enabled)(true);
1357 ComAssertComRCThrowRC(rc);
1358 rc = nwVBox->COMSETTER(AdapterType)(static_cast<NetworkAdapterType_T>(tt1));
1359 ComAssertComRCThrowRC(rc);
1360 }
1361 }
1362 /* Now its time to register the machine before we add any hard disks */
1363 rc = mVirtualBox->RegisterMachine(newMachine);
1364 ComAssertComRCThrowRC(rc);
1365
1366 /* @todo: Unregister on failure */
1367#if 0
1368 vbox.UnregisterMachine (machineId);
1369 if (vbox.isOk())
1370 mMachine.DeleteSettings();
1371 return false;
1372#endif
1373 }
1374
1375 return S_OK;
1376}
1377
1378HRESULT Appliance::searchUniqueVMName(std::string& aName)
1379{
1380 IMachine *machine = NULL;
1381 char *tmpName = RTStrDup(aName.c_str());
1382 int i = 1;
1383 /* @todo: Maybe to cost intensive; try to find a lighter way */
1384 while (mVirtualBox->FindMachine(Bstr(tmpName), &machine) != VBOX_E_OBJECT_NOT_FOUND)
1385 {
1386 RTStrFree(tmpName);
1387 RTStrAPrintf(&tmpName, "%s_%d", aName.c_str(), i);
1388 ++i;
1389 }
1390 aName = tmpName;
1391 RTStrFree(tmpName);
1392
1393 return S_OK;
1394}
1395
1396// IVirtualSystemDescription constructor / destructor
1397////////////////////////////////////////////////////////////////////////////////
1398
1399DEFINE_EMPTY_CTOR_DTOR(VirtualSystemDescription)
1400struct shutup3 {};
1401
1402struct VirtualSystemDescription::Data
1403{
1404 list<VirtualSystemDescriptionEntry> descriptions;
1405};
1406
1407HRESULT VirtualSystemDescription::init()
1408{
1409 /* Enclose the state transition NotReady->InInit->Ready */
1410 AutoInitSpan autoInitSpan(this);
1411 AssertReturn(autoInitSpan.isOk(), E_FAIL);
1412
1413 /* Initialize data */
1414 m = new Data();
1415
1416 /* Confirm a successful initialization */
1417 autoInitSpan.setSucceeded();
1418
1419 return S_OK;
1420}
1421
1422void VirtualSystemDescription::uninit()
1423{
1424 delete m;
1425 m = NULL;
1426}
1427
1428STDMETHODIMP VirtualSystemDescription::GetDescription(ComSafeArrayOut(VirtualSystemDescriptionType_T, aTypes),
1429 ComSafeArrayOut(ULONG, aRefs),
1430 ComSafeArrayOut(BSTR, aOrigValues),
1431 ComSafeArrayOut(BSTR, aAutoValues),
1432 ComSafeArrayOut(BSTR, aConfigurations))
1433{
1434 if (ComSafeArrayOutIsNull(aTypes) ||
1435 ComSafeArrayOutIsNull(aRefs) ||
1436 ComSafeArrayOutIsNull(aOrigValues) ||
1437 ComSafeArrayOutIsNull(aAutoValues) ||
1438 ComSafeArrayOutIsNull(aConfigurations))
1439 return E_POINTER;
1440
1441 AutoCaller autoCaller(this);
1442 CheckComRCReturnRC(autoCaller.rc());
1443
1444 AutoReadLock alock(this);
1445
1446 ULONG c = (ULONG)m->descriptions.size();
1447 com::SafeArray<VirtualSystemDescriptionType_T> sfaTypes(c);
1448 com::SafeArray<ULONG> sfaRefs(c);
1449 com::SafeArray<BSTR> sfaOrigValues(c);
1450 com::SafeArray<BSTR> sfaAutoValues(c);
1451 com::SafeArray<BSTR> sfaConfigurations(c);
1452
1453 list<VirtualSystemDescriptionEntry>::const_iterator it;
1454 size_t i = 0;
1455 for (it = m->descriptions.begin();
1456 it != m->descriptions.end();
1457 ++it, ++i)
1458 {
1459 VirtualSystemDescriptionEntry vsde = (*it);
1460 /* Types */
1461 sfaTypes [i] = vsde.type;
1462 /* Refs */
1463 sfaRefs [i] = vsde.ref;
1464 /* Original value */
1465 Bstr bstr = Utf8Str(vsde.strOriginalValue.c_str());
1466 bstr.cloneTo(&sfaOrigValues [i]);
1467 /* Auto value */
1468 bstr = Utf8Str(vsde.strAutoValue.c_str());
1469 bstr.cloneTo(&sfaAutoValues [i]);
1470 /* Configuration */
1471 bstr = Utf8Str(vsde.strConfiguration.c_str());
1472 bstr.cloneTo(&sfaConfigurations [i]);
1473 }
1474
1475 sfaTypes.detachTo(ComSafeArrayOutArg(aTypes));
1476 sfaRefs.detachTo(ComSafeArrayOutArg(aRefs));
1477 sfaOrigValues.detachTo(ComSafeArrayOutArg(aOrigValues));
1478 sfaAutoValues.detachTo(ComSafeArrayOutArg(aAutoValues));
1479 sfaConfigurations.detachTo(ComSafeArrayOutArg(aConfigurations));
1480
1481 return S_OK;
1482}
1483
1484STDMETHODIMP VirtualSystemDescription::SetFinalValues(ComSafeArrayIn(IN_BSTR, aFinalValues))
1485{
1486 CheckComArgSafeArrayNotNull(aFinalValues);
1487
1488 AutoCaller autoCaller(this);
1489 CheckComRCReturnRC(autoCaller.rc());
1490
1491 AutoWriteLock alock(this);
1492
1493 com::SafeArray <IN_BSTR> values(ComSafeArrayInArg(aFinalValues));
1494 if (values.size() != m->descriptions.size())
1495 return E_INVALIDARG;
1496
1497 list<VirtualSystemDescriptionEntry>::const_iterator it;
1498 size_t i = 0;
1499 for (it = m->descriptions.begin();
1500 it != m->descriptions.end();
1501 ++it, ++i)
1502 {
1503 VirtualSystemDescriptionEntry vsde = (*it);
1504 vsde.strFinalValue = Utf8Str(values[i]).raw();
1505 }
1506
1507 return S_OK;
1508}
1509
1510void VirtualSystemDescription::addEntry(VirtualSystemDescriptionType_T aType, ULONG aRef, std::string aOrigValue, std::string aAutoValue)
1511{
1512 VirtualSystemDescriptionEntry vsde;
1513 vsde.type = aType;
1514 vsde.ref = aRef;
1515 vsde.strOriginalValue = aOrigValue;
1516 vsde.strAutoValue = aAutoValue;
1517 /* For now we add the auto value as final value also */
1518 vsde.strFinalValue = aAutoValue;
1519
1520 m->descriptions.push_back(vsde);
1521}
1522
1523list<VirtualSystemDescriptionEntry> VirtualSystemDescription::findByType(VirtualSystemDescriptionType_T aType)
1524{
1525 list<VirtualSystemDescriptionEntry> vsd;
1526
1527 list<VirtualSystemDescriptionEntry>::const_iterator it;
1528 for (it = m->descriptions.begin();
1529 it != m->descriptions.end();
1530 ++it)
1531 if (it->type == aType)
1532 vsd.push_back(*it);
1533
1534 return vsd;
1535}
1536
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