~ubuntu-branches/ubuntu/raring/virtualbox-ose/raring

« back to all changes in this revision

Viewing changes to src/VBox/Main/ApplianceImplExport.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Felix Geyer
  • Date: 2011-01-30 23:27:25 UTC
  • mfrom: (0.3.12 upstream)
  • Revision ID: james.westby@ubuntu.com-20110130232725-2ouajjd2ggdet0zd
Tags: 4.0.2-dfsg-1ubuntu1
* Merge from Debian unstable, remaining changes:
  - Add Apport hook.
    - debian/virtualbox-ose.files/source_virtualbox-ose.py
    - debian/virtualbox-ose.install
  - Drop *-source packages.
* Drop ubuntu-01-fix-build-gcc45.patch, fixed upstream.
* Drop ubuntu-02-as-needed.patch, added to the Debian package.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* $Id: ApplianceImplExport.cpp $ */
2
 
/** @file
3
 
 *
4
 
 * IAppliance and IVirtualSystem COM class implementations.
5
 
 */
6
 
 
7
 
/*
8
 
 * Copyright (C) 2008-2010 Oracle Corporation
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
 
 
19
 
#include <iprt/path.h>
20
 
#include <iprt/dir.h>
21
 
#include <iprt/param.h>
22
 
#include <iprt/s3.h>
23
 
#include <iprt/manifest.h>
24
 
 
25
 
#include <VBox/version.h>
26
 
 
27
 
#include "ApplianceImpl.h"
28
 
#include "VirtualBoxImpl.h"
29
 
 
30
 
#include "ProgressImpl.h"
31
 
#include "MachineImpl.h"
32
 
 
33
 
#include "AutoCaller.h"
34
 
#include "Logging.h"
35
 
 
36
 
#include "ApplianceImplPrivate.h"
37
 
 
38
 
using namespace std;
39
 
 
40
 
////////////////////////////////////////////////////////////////////////////////
41
 
//
42
 
// IMachine public methods
43
 
//
44
 
////////////////////////////////////////////////////////////////////////////////
45
 
 
46
 
// This code is here so we won't have to include the appliance headers in the
47
 
// IMachine implementation, and we also need to access private appliance data.
48
 
 
49
 
/**
50
 
* Public method implementation.
51
 
* @param appliance
52
 
* @return
53
 
*/
54
 
 
55
 
STDMETHODIMP Machine::Export(IAppliance *aAppliance, IVirtualSystemDescription **aDescription)
56
 
{
57
 
    HRESULT rc = S_OK;
58
 
 
59
 
    if (!aAppliance)
60
 
        return E_POINTER;
61
 
 
62
 
    AutoCaller autoCaller(this);
63
 
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
64
 
 
65
 
    ComObjPtr<VirtualSystemDescription> pNewDesc;
66
 
 
67
 
    try
68
 
    {
69
 
        // create a new virtual system to store in the appliance
70
 
        rc = pNewDesc.createObject();
71
 
        if (FAILED(rc)) throw rc;
72
 
        rc = pNewDesc->init();
73
 
        if (FAILED(rc)) throw rc;
74
 
 
75
 
        // store the machine object so we can dump the XML in Appliance::Write()
76
 
        pNewDesc->m->pMachine = this;
77
 
 
78
 
        // now fill it with description items
79
 
        Bstr bstrName1;
80
 
        Bstr bstrDescription;
81
 
        Bstr bstrGuestOSType;
82
 
        uint32_t cCPUs;
83
 
        uint32_t ulMemSizeMB;
84
 
        BOOL fUSBEnabled;
85
 
        BOOL fAudioEnabled;
86
 
        AudioControllerType_T audioController;
87
 
 
88
 
        ComPtr<IUSBController> pUsbController;
89
 
        ComPtr<IAudioAdapter> pAudioAdapter;
90
 
 
91
 
        // first, call the COM methods, as they request locks
92
 
        rc = COMGETTER(USBController)(pUsbController.asOutParam());
93
 
        if (FAILED(rc))
94
 
            fUSBEnabled = false;
95
 
        else
96
 
            rc = pUsbController->COMGETTER(Enabled)(&fUSBEnabled);
97
 
 
98
 
        // request the machine lock while acessing internal members
99
 
        AutoReadLock alock1(this COMMA_LOCKVAL_SRC_POS);
100
 
 
101
 
        pAudioAdapter = mAudioAdapter;
102
 
        rc = pAudioAdapter->COMGETTER(Enabled)(&fAudioEnabled);
103
 
        if (FAILED(rc)) throw rc;
104
 
        rc = pAudioAdapter->COMGETTER(AudioController)(&audioController);
105
 
        if (FAILED(rc)) throw rc;
106
 
 
107
 
        // get name
108
 
        bstrName1 = mUserData->mName;
109
 
        // get description
110
 
        bstrDescription = mUserData->mDescription;
111
 
        // get guest OS
112
 
        bstrGuestOSType = mUserData->mOSTypeId;
113
 
        // CPU count
114
 
        cCPUs = mHWData->mCPUCount;
115
 
        // memory size in MB
116
 
        ulMemSizeMB = mHWData->mMemorySize;
117
 
        // VRAM size?
118
 
        // BIOS settings?
119
 
        // 3D acceleration enabled?
120
 
        // hardware virtualization enabled?
121
 
        // nested paging enabled?
122
 
        // HWVirtExVPIDEnabled?
123
 
        // PAEEnabled?
124
 
        // snapshotFolder?
125
 
        // VRDPServer?
126
 
 
127
 
        /* Guest OS type */
128
 
        Utf8Str strOsTypeVBox(bstrGuestOSType);
129
 
        ovf::CIMOSType_T cim = convertVBoxOSType2CIMOSType(strOsTypeVBox.c_str());
130
 
        pNewDesc->addEntry(VirtualSystemDescriptionType_OS,
131
 
                           "",
132
 
                           Utf8StrFmt("%RI32", cim),
133
 
                           strOsTypeVBox);
134
 
 
135
 
        /* VM name */
136
 
        Utf8Str strVMName(bstrName1);
137
 
        pNewDesc->addEntry(VirtualSystemDescriptionType_Name,
138
 
                           "",
139
 
                           strVMName,
140
 
                           strVMName);
141
 
 
142
 
        // description
143
 
        Utf8Str strDescription(bstrDescription);
144
 
        pNewDesc->addEntry(VirtualSystemDescriptionType_Description,
145
 
                           "",
146
 
                           strDescription,
147
 
                           strDescription);
148
 
 
149
 
        /* CPU count*/
150
 
        Utf8Str strCpuCount = Utf8StrFmt("%RI32", cCPUs);
151
 
        pNewDesc->addEntry(VirtualSystemDescriptionType_CPU,
152
 
                           "",
153
 
                           strCpuCount,
154
 
                           strCpuCount);
155
 
 
156
 
        /* Memory */
157
 
        Utf8Str strMemory = Utf8StrFmt("%RI64", (uint64_t)ulMemSizeMB * _1M);
158
 
        pNewDesc->addEntry(VirtualSystemDescriptionType_Memory,
159
 
                           "",
160
 
                           strMemory,
161
 
                           strMemory);
162
 
 
163
 
        // the one VirtualBox IDE controller has two channels with two ports each, which is
164
 
        // considered two IDE controllers with two ports each by OVF, so export it as two
165
 
        int32_t lIDEControllerPrimaryIndex = 0;
166
 
        int32_t lIDEControllerSecondaryIndex = 0;
167
 
        int32_t lSATAControllerIndex = 0;
168
 
        int32_t lSCSIControllerIndex = 0;
169
 
 
170
 
        /* Fetch all available storage controllers */
171
 
        com::SafeIfaceArray<IStorageController> nwControllers;
172
 
        rc = COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(nwControllers));
173
 
        if (FAILED(rc)) throw rc;
174
 
 
175
 
        ComPtr<IStorageController> pIDEController;
176
 
        ComPtr<IStorageController> pSATAController;
177
 
        ComPtr<IStorageController> pSCSIController;
178
 
        ComPtr<IStorageController> pSASController;
179
 
        for (size_t j = 0; j < nwControllers.size(); ++j)
180
 
        {
181
 
            StorageBus_T eType;
182
 
            rc = nwControllers[j]->COMGETTER(Bus)(&eType);
183
 
            if (FAILED(rc)) throw rc;
184
 
            if (   eType == StorageBus_IDE
185
 
                && pIDEController.isNull())
186
 
                pIDEController = nwControllers[j];
187
 
            else if (   eType == StorageBus_SATA
188
 
                     && pSATAController.isNull())
189
 
                pSATAController = nwControllers[j];
190
 
            else if (   eType == StorageBus_SCSI
191
 
                     && pSATAController.isNull())
192
 
                pSCSIController = nwControllers[j];
193
 
            else if (   eType == StorageBus_SAS
194
 
                     && pSASController.isNull())
195
 
                pSASController = nwControllers[j];
196
 
        }
197
 
 
198
 
//     <const name="HardDiskControllerIDE" value="6" />
199
 
        if (!pIDEController.isNull())
200
 
        {
201
 
            Utf8Str strVbox;
202
 
            StorageControllerType_T ctlr;
203
 
            rc = pIDEController->COMGETTER(ControllerType)(&ctlr);
204
 
            if (FAILED(rc)) throw rc;
205
 
            switch(ctlr)
206
 
            {
207
 
                case StorageControllerType_PIIX3: strVbox = "PIIX3"; break;
208
 
                case StorageControllerType_PIIX4: strVbox = "PIIX4"; break;
209
 
                case StorageControllerType_ICH6: strVbox = "ICH6"; break;
210
 
            }
211
 
 
212
 
            if (strVbox.length())
213
 
            {
214
 
                lIDEControllerPrimaryIndex = (int32_t)pNewDesc->m->llDescriptions.size();
215
 
                pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerIDE,
216
 
                                   Utf8StrFmt("%d", lIDEControllerPrimaryIndex),        // strRef
217
 
                                   strVbox,     // aOvfValue
218
 
                                   strVbox);    // aVboxValue
219
 
                lIDEControllerSecondaryIndex = lIDEControllerPrimaryIndex + 1;
220
 
                pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerIDE,
221
 
                                   Utf8StrFmt("%d", lIDEControllerSecondaryIndex),
222
 
                                   strVbox,
223
 
                                   strVbox);
224
 
            }
225
 
        }
226
 
 
227
 
//     <const name="HardDiskControllerSATA" value="7" />
228
 
        if (!pSATAController.isNull())
229
 
        {
230
 
            Utf8Str strVbox = "AHCI";
231
 
            lSATAControllerIndex = (int32_t)pNewDesc->m->llDescriptions.size();
232
 
            pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSATA,
233
 
                               Utf8StrFmt("%d", lSATAControllerIndex),
234
 
                               strVbox,
235
 
                               strVbox);
236
 
        }
237
 
 
238
 
//     <const name="HardDiskControllerSCSI" value="8" />
239
 
        if (!pSCSIController.isNull())
240
 
        {
241
 
            StorageControllerType_T ctlr;
242
 
            rc = pSCSIController->COMGETTER(ControllerType)(&ctlr);
243
 
            if (SUCCEEDED(rc))
244
 
            {
245
 
                Utf8Str strVbox = "LsiLogic";       // the default in VBox
246
 
                switch(ctlr)
247
 
                {
248
 
                    case StorageControllerType_LsiLogic: strVbox = "LsiLogic"; break;
249
 
                    case StorageControllerType_BusLogic: strVbox = "BusLogic"; break;
250
 
                }
251
 
                lSCSIControllerIndex = (int32_t)pNewDesc->m->llDescriptions.size();
252
 
                pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSCSI,
253
 
                                   Utf8StrFmt("%d", lSCSIControllerIndex),
254
 
                                   strVbox,
255
 
                                   strVbox);
256
 
            }
257
 
            else
258
 
                throw rc;
259
 
        }
260
 
 
261
 
        if (!pSASController.isNull())
262
 
        {
263
 
            // VirtualBox considers the SAS controller a class of its own but in OVF
264
 
            // it should be a SCSI controller
265
 
            Utf8Str strVbox = "LsiLogicSas";
266
 
            lSCSIControllerIndex = (int32_t)pNewDesc->m->llDescriptions.size();
267
 
            pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSAS,
268
 
                               Utf8StrFmt("%d", lSCSIControllerIndex),
269
 
                               strVbox,
270
 
                               strVbox);
271
 
        }
272
 
 
273
 
//     <const name="HardDiskImage" value="9" />
274
 
//     <const name="Floppy" value="18" />
275
 
//     <const name="CDROM" value="19" />
276
 
 
277
 
        MediaData::AttachmentList::iterator itA;
278
 
        for (itA = mMediaData->mAttachments.begin();
279
 
             itA != mMediaData->mAttachments.end();
280
 
             ++itA)
281
 
        {
282
 
            ComObjPtr<MediumAttachment> pHDA = *itA;
283
 
 
284
 
            // the attachment's data
285
 
            ComPtr<IMedium> pMedium;
286
 
            ComPtr<IStorageController> ctl;
287
 
            Bstr controllerName;
288
 
 
289
 
            rc = pHDA->COMGETTER(Controller)(controllerName.asOutParam());
290
 
            if (FAILED(rc)) throw rc;
291
 
 
292
 
            rc = GetStorageControllerByName(controllerName, ctl.asOutParam());
293
 
            if (FAILED(rc)) throw rc;
294
 
 
295
 
            StorageBus_T storageBus;
296
 
            DeviceType_T deviceType;
297
 
            LONG lChannel;
298
 
            LONG lDevice;
299
 
 
300
 
            rc = ctl->COMGETTER(Bus)(&storageBus);
301
 
            if (FAILED(rc)) throw rc;
302
 
 
303
 
            rc = pHDA->COMGETTER(Type)(&deviceType);
304
 
            if (FAILED(rc)) throw rc;
305
 
 
306
 
            rc = pHDA->COMGETTER(Medium)(pMedium.asOutParam());
307
 
            if (FAILED(rc)) throw rc;
308
 
 
309
 
            rc = pHDA->COMGETTER(Port)(&lChannel);
310
 
            if (FAILED(rc)) throw rc;
311
 
 
312
 
            rc = pHDA->COMGETTER(Device)(&lDevice);
313
 
            if (FAILED(rc)) throw rc;
314
 
 
315
 
            Utf8Str strTargetVmdkName;
316
 
            Utf8Str strLocation;
317
 
            ULONG64 ullSize = 0;
318
 
 
319
 
            if (    deviceType == DeviceType_HardDisk
320
 
                 && pMedium
321
 
               )
322
 
            {
323
 
                Bstr bstrLocation;
324
 
                rc = pMedium->COMGETTER(Location)(bstrLocation.asOutParam());
325
 
                if (FAILED(rc)) throw rc;
326
 
                strLocation = bstrLocation;
327
 
 
328
 
                // find the source's base medium for two things:
329
 
                // 1) we'll use its name to determine the name of the target disk, which is readable,
330
 
                //    as opposed to the UUID filename of a differencing image, if pMedium is one
331
 
                // 2) we need the size of the base image so we can give it to addEntry(), and later
332
 
                //    on export, the progress will be based on that (and not the diff image)
333
 
                ComPtr<IMedium> pBaseMedium;
334
 
                rc = pMedium->COMGETTER(Base)(pBaseMedium.asOutParam());
335
 
                        // returns pMedium if there are no diff images
336
 
                if (FAILED(rc)) throw rc;
337
 
 
338
 
                Bstr bstrBaseName;
339
 
                rc = pBaseMedium->COMGETTER(Name)(bstrBaseName.asOutParam());
340
 
                if (FAILED(rc)) throw rc;
341
 
 
342
 
                strTargetVmdkName = bstrBaseName;
343
 
                strTargetVmdkName.stripExt();
344
 
                strTargetVmdkName.append(".vmdk");
345
 
 
346
 
                // force reading state, or else size will be returned as 0
347
 
                MediumState_T ms;
348
 
                rc = pBaseMedium->RefreshState(&ms);
349
 
                if (FAILED(rc)) throw rc;
350
 
 
351
 
                rc = pBaseMedium->COMGETTER(Size)(&ullSize);
352
 
                if (FAILED(rc)) throw rc;
353
 
            }
354
 
 
355
 
            // and how this translates to the virtual system
356
 
            int32_t lControllerVsys = 0;
357
 
            LONG lChannelVsys;
358
 
 
359
 
            switch (storageBus)
360
 
            {
361
 
                case StorageBus_IDE:
362
 
                    // this is the exact reverse to what we're doing in Appliance::taskThreadImportMachines,
363
 
                    // and it must be updated when that is changed!
364
 
                    // Before 3.2 we exported one IDE controller with channel 0-3, but we now maintain
365
 
                    // compatibility with what VMware does and export two IDE controllers with two channels each
366
 
 
367
 
                    if (lChannel == 0 && lDevice == 0)      // primary master
368
 
                    {
369
 
                        lControllerVsys = lIDEControllerPrimaryIndex;
370
 
                        lChannelVsys = 0;
371
 
                    }
372
 
                    else if (lChannel == 0 && lDevice == 1) // primary slave
373
 
                    {
374
 
                        lControllerVsys = lIDEControllerPrimaryIndex;
375
 
                        lChannelVsys = 1;
376
 
                    }
377
 
                    else if (lChannel == 1 && lDevice == 0) // secondary master; by default this is the CD-ROM but as of VirtualBox 3.1 that can change
378
 
                    {
379
 
                        lControllerVsys = lIDEControllerSecondaryIndex;
380
 
                        lChannelVsys = 0;
381
 
                    }
382
 
                    else if (lChannel == 1 && lDevice == 1) // secondary slave
383
 
                    {
384
 
                        lControllerVsys = lIDEControllerSecondaryIndex;
385
 
                        lChannelVsys = 1;
386
 
                    }
387
 
                    else
388
 
                        throw setError(VBOX_E_NOT_SUPPORTED,
389
 
                                    tr("Cannot handle medium attachment: channel is %d, device is %d"), lChannel, lDevice);
390
 
                break;
391
 
 
392
 
                case StorageBus_SATA:
393
 
                    lChannelVsys = lChannel;        // should be between 0 and 29
394
 
                    lControllerVsys = lSATAControllerIndex;
395
 
                break;
396
 
 
397
 
                case StorageBus_SCSI:
398
 
                case StorageBus_SAS:
399
 
                    lChannelVsys = lChannel;        // should be between 0 and 15
400
 
                    lControllerVsys = lSCSIControllerIndex;
401
 
                break;
402
 
 
403
 
                case StorageBus_Floppy:
404
 
                    lChannelVsys = 0;
405
 
                    lControllerVsys = 0;
406
 
                break;
407
 
 
408
 
                default:
409
 
                    throw setError(VBOX_E_NOT_SUPPORTED,
410
 
                                tr("Cannot handle medium attachment: storageBus is %d, channel is %d, device is %d"), storageBus, lChannel, lDevice);
411
 
                break;
412
 
            }
413
 
 
414
 
            Utf8StrFmt strExtra("controller=%RI32;channel=%RI32", lControllerVsys, lChannelVsys);
415
 
            Utf8Str strEmpty;
416
 
 
417
 
            switch (deviceType)
418
 
            {
419
 
                case DeviceType_HardDisk:
420
 
                    Log(("Adding VirtualSystemDescriptionType_HardDiskImage, disk size: %RI64\n", ullSize));
421
 
                    pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskImage,
422
 
                                       strTargetVmdkName,   // disk ID: let's use the name
423
 
                                       strTargetVmdkName,   // OVF value:
424
 
                                       strLocation, // vbox value: media path
425
 
                                       (uint32_t)(ullSize / _1M),
426
 
                                       strExtra);
427
 
                break;
428
 
 
429
 
                case DeviceType_DVD:
430
 
                    pNewDesc->addEntry(VirtualSystemDescriptionType_CDROM,
431
 
                                       strEmpty,   // disk ID
432
 
                                       strEmpty,   // OVF value
433
 
                                       strEmpty, // vbox value
434
 
                                       1,           // ulSize
435
 
                                       strExtra);
436
 
                break;
437
 
 
438
 
                case DeviceType_Floppy:
439
 
                    pNewDesc->addEntry(VirtualSystemDescriptionType_Floppy,
440
 
                                       strEmpty,      // disk ID
441
 
                                       strEmpty,      // OVF value
442
 
                                       strEmpty,      // vbox value
443
 
                                       1,       // ulSize
444
 
                                       strExtra);
445
 
                break;
446
 
            }
447
 
        }
448
 
 
449
 
//     <const name="NetworkAdapter" />
450
 
        size_t a;
451
 
        for (a = 0;
452
 
             a < SchemaDefs::NetworkAdapterCount;
453
 
             ++a)
454
 
        {
455
 
            ComPtr<INetworkAdapter> pNetworkAdapter;
456
 
            BOOL fEnabled;
457
 
            NetworkAdapterType_T adapterType;
458
 
            NetworkAttachmentType_T attachmentType;
459
 
 
460
 
            rc = GetNetworkAdapter((ULONG)a, pNetworkAdapter.asOutParam());
461
 
            if (FAILED(rc)) throw rc;
462
 
            /* Enable the network card & set the adapter type */
463
 
            rc = pNetworkAdapter->COMGETTER(Enabled)(&fEnabled);
464
 
            if (FAILED(rc)) throw rc;
465
 
 
466
 
            if (fEnabled)
467
 
            {
468
 
                Utf8Str strAttachmentType;
469
 
 
470
 
                rc = pNetworkAdapter->COMGETTER(AdapterType)(&adapterType);
471
 
                if (FAILED(rc)) throw rc;
472
 
 
473
 
                rc = pNetworkAdapter->COMGETTER(AttachmentType)(&attachmentType);
474
 
                if (FAILED(rc)) throw rc;
475
 
 
476
 
                switch (attachmentType)
477
 
                {
478
 
                    case NetworkAttachmentType_Null:
479
 
                        strAttachmentType = "Null";
480
 
                    break;
481
 
 
482
 
                    case NetworkAttachmentType_NAT:
483
 
                        strAttachmentType = "NAT";
484
 
                    break;
485
 
 
486
 
                    case NetworkAttachmentType_Bridged:
487
 
                        strAttachmentType = "Bridged";
488
 
                    break;
489
 
 
490
 
                    case NetworkAttachmentType_Internal:
491
 
                        strAttachmentType = "Internal";
492
 
                    break;
493
 
 
494
 
                    case NetworkAttachmentType_HostOnly:
495
 
                        strAttachmentType = "HostOnly";
496
 
                    break;
497
 
                }
498
 
 
499
 
                pNewDesc->addEntry(VirtualSystemDescriptionType_NetworkAdapter,
500
 
                                   "",      // ref
501
 
                                   strAttachmentType,      // orig
502
 
                                   Utf8StrFmt("%RI32", (uint32_t)adapterType),   // conf
503
 
                                   0,
504
 
                                   Utf8StrFmt("type=%s", strAttachmentType.c_str()));       // extra conf
505
 
            }
506
 
        }
507
 
 
508
 
//     <const name="USBController"  />
509
 
#ifdef VBOX_WITH_USB
510
 
        if (fUSBEnabled)
511
 
            pNewDesc->addEntry(VirtualSystemDescriptionType_USBController, "", "", "");
512
 
#endif /* VBOX_WITH_USB */
513
 
 
514
 
//     <const name="SoundCard"  />
515
 
        if (fAudioEnabled)
516
 
            pNewDesc->addEntry(VirtualSystemDescriptionType_SoundCard,
517
 
                               "",
518
 
                               "ensoniq1371",       // this is what OVFTool writes and VMware supports
519
 
                               Utf8StrFmt("%RI32", audioController));
520
 
 
521
 
        // finally, add the virtual system to the appliance
522
 
        Appliance *pAppliance = static_cast<Appliance*>(aAppliance);
523
 
        AutoCaller autoCaller1(pAppliance);
524
 
        if (FAILED(autoCaller1.rc())) return autoCaller1.rc();
525
 
 
526
 
        /* We return the new description to the caller */
527
 
        ComPtr<IVirtualSystemDescription> copy(pNewDesc);
528
 
        copy.queryInterfaceTo(aDescription);
529
 
 
530
 
        AutoWriteLock alock(pAppliance COMMA_LOCKVAL_SRC_POS);
531
 
 
532
 
        pAppliance->m->virtualSystemDescriptions.push_back(pNewDesc);
533
 
    }
534
 
    catch(HRESULT arc)
535
 
    {
536
 
        rc = arc;
537
 
    }
538
 
 
539
 
    return rc;
540
 
}
541
 
 
542
 
////////////////////////////////////////////////////////////////////////////////
543
 
//
544
 
// IAppliance public methods
545
 
//
546
 
////////////////////////////////////////////////////////////////////////////////
547
 
 
548
 
/**
549
 
 * Public method implementation.
550
 
 * @param format
551
 
 * @param path
552
 
 * @param aProgress
553
 
 * @return
554
 
 */
555
 
STDMETHODIMP Appliance::Write(IN_BSTR format, IN_BSTR path, IProgress **aProgress)
556
 
{
557
 
    if (!path) return E_POINTER;
558
 
    CheckComArgOutPointerValid(aProgress);
559
 
 
560
 
    AutoCaller autoCaller(this);
561
 
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
562
 
 
563
 
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
564
 
 
565
 
    // do not allow entering this method if the appliance is busy reading or writing
566
 
    if (!isApplianceIdle())
567
 
        return E_ACCESSDENIED;
568
 
 
569
 
    // see if we can handle this file; for now we insist it has an ".ovf" extension
570
 
    Utf8Str strPath = path;
571
 
    if (!strPath.endsWith(".ovf", Utf8Str::CaseInsensitive))
572
 
        return setError(VBOX_E_FILE_ERROR,
573
 
                        tr("Appliance file must have .ovf extension"));
574
 
 
575
 
    Utf8Str strFormat(format);
576
 
    OVFFormat ovfF;
577
 
    if (strFormat == "ovf-0.9")
578
 
        ovfF = OVF_0_9;
579
 
    else if (strFormat == "ovf-1.0")
580
 
        ovfF = OVF_1_0;
581
 
    else
582
 
        return setError(VBOX_E_FILE_ERROR,
583
 
                        tr("Invalid format \"%s\" specified"), strFormat.c_str());
584
 
 
585
 
    ComObjPtr<Progress> progress;
586
 
    HRESULT rc = S_OK;
587
 
    try
588
 
    {
589
 
        /* Parse all necessary info out of the URI */
590
 
        parseURI(strPath, m->locInfo);
591
 
        rc = writeImpl(ovfF, m->locInfo, progress);
592
 
    }
593
 
    catch (HRESULT aRC)
594
 
    {
595
 
        rc = aRC;
596
 
    }
597
 
 
598
 
    if (SUCCEEDED(rc))
599
 
        /* Return progress to the caller */
600
 
        progress.queryInterfaceTo(aProgress);
601
 
 
602
 
    return rc;
603
 
}
604
 
 
605
 
////////////////////////////////////////////////////////////////////////////////
606
 
//
607
 
// Appliance private methods
608
 
//
609
 
////////////////////////////////////////////////////////////////////////////////
610
 
 
611
 
/**
612
 
 * Implementation for writing out the OVF to disk. This starts a new thread which will call
613
 
 * Appliance::taskThreadWriteOVF().
614
 
 *
615
 
 * This is in a separate private method because it is used from two locations:
616
 
 *
617
 
 * 1) from the public Appliance::Write().
618
 
 * 2) from Appliance::writeS3(), which got called from a previous instance of Appliance::taskThreadWriteOVF().
619
 
 *
620
 
 * @param aFormat
621
 
 * @param aLocInfo
622
 
 * @param aProgress
623
 
 * @return
624
 
 */
625
 
HRESULT Appliance::writeImpl(OVFFormat aFormat, const LocationInfo &aLocInfo, ComObjPtr<Progress> &aProgress)
626
 
{
627
 
    HRESULT rc = S_OK;
628
 
    try
629
 
    {
630
 
        rc = setUpProgress(aProgress,
631
 
                           BstrFmt(tr("Export appliance '%s'"), aLocInfo.strPath.c_str()),
632
 
                           (aLocInfo.storageType == VFSType_File) ? WriteFile : WriteS3);
633
 
 
634
 
        /* Initialize our worker task */
635
 
        std::auto_ptr<TaskOVF> task(new TaskOVF(this, TaskOVF::Write, aLocInfo, aProgress));
636
 
        /* The OVF version to write */
637
 
        task->enFormat = aFormat;
638
 
 
639
 
        rc = task->startThread();
640
 
        if (FAILED(rc)) throw rc;
641
 
 
642
 
        /* Don't destruct on success */
643
 
        task.release();
644
 
    }
645
 
    catch (HRESULT aRC)
646
 
    {
647
 
        rc = aRC;
648
 
    }
649
 
 
650
 
    return rc;
651
 
}
652
 
 
653
 
/**
654
 
 * Called from Appliance::writeFS() for each virtual system (machine) that needs XML written out.
655
 
 *
656
 
 * @param elmToAddVirtualSystemsTo XML element to append elements to.
657
 
 * @param pllElementsWithUuidAttributes out: list of XML elements produced here with UUID attributes for quick fixing by caller later
658
 
 * @param vsdescThis The IVirtualSystemDescription instance for which to write XML.
659
 
 * @param enFormat OVF format (0.9 or 1.0).
660
 
 * @param stack Structure for temporary private data shared with caller.
661
 
 */
662
 
void Appliance::buildXMLForOneVirtualSystem(xml::ElementNode &elmToAddVirtualSystemsTo,
663
 
                                            std::list<xml::ElementNode*> *pllElementsWithUuidAttributes,
664
 
                                            ComObjPtr<VirtualSystemDescription> &vsdescThis,
665
 
                                            OVFFormat enFormat,
666
 
                                            XMLStack &stack)
667
 
{
668
 
    LogFlowFunc(("ENTER appliance %p\n", this));
669
 
 
670
 
    xml::ElementNode *pelmVirtualSystem;
671
 
    if (enFormat == OVF_0_9)
672
 
    {
673
 
        // <Section xsi:type="ovf:NetworkSection_Type">
674
 
        pelmVirtualSystem = elmToAddVirtualSystemsTo.createChild("Content");
675
 
        pelmVirtualSystem->setAttribute("xsi:type", "ovf:VirtualSystem_Type");
676
 
    }
677
 
    else
678
 
        pelmVirtualSystem = elmToAddVirtualSystemsTo.createChild("VirtualSystem");
679
 
 
680
 
    /*xml::ElementNode *pelmVirtualSystemInfo =*/ pelmVirtualSystem->createChild("Info")->addContent("A virtual machine");
681
 
 
682
 
    std::list<VirtualSystemDescriptionEntry*> llName = vsdescThis->findByType(VirtualSystemDescriptionType_Name);
683
 
    if (llName.size() != 1)
684
 
        throw setError(VBOX_E_NOT_SUPPORTED,
685
 
                        tr("Missing VM name"));
686
 
    Utf8Str &strVMName = llName.front()->strVboxCurrent;
687
 
    pelmVirtualSystem->setAttribute("ovf:id", strVMName);
688
 
 
689
 
    // product info
690
 
    std::list<VirtualSystemDescriptionEntry*> llProduct = vsdescThis->findByType(VirtualSystemDescriptionType_Product);
691
 
    std::list<VirtualSystemDescriptionEntry*> llProductUrl = vsdescThis->findByType(VirtualSystemDescriptionType_ProductUrl);
692
 
    std::list<VirtualSystemDescriptionEntry*> llVendor = vsdescThis->findByType(VirtualSystemDescriptionType_Vendor);
693
 
    std::list<VirtualSystemDescriptionEntry*> llVendorUrl = vsdescThis->findByType(VirtualSystemDescriptionType_VendorUrl);
694
 
    std::list<VirtualSystemDescriptionEntry*> llVersion = vsdescThis->findByType(VirtualSystemDescriptionType_Version);
695
 
    bool fProduct = llProduct.size() && !llProduct.front()->strVboxCurrent.isEmpty();
696
 
    bool fProductUrl = llProductUrl.size() && !llProductUrl.front()->strVboxCurrent.isEmpty();
697
 
    bool fVendor = llVendor.size() && !llVendor.front()->strVboxCurrent.isEmpty();
698
 
    bool fVendorUrl = llVendorUrl.size() && !llVendorUrl.front()->strVboxCurrent.isEmpty();
699
 
    bool fVersion = llVersion.size() && !llVersion.front()->strVboxCurrent.isEmpty();
700
 
    if (fProduct ||
701
 
        fProductUrl ||
702
 
        fVersion ||
703
 
        fVendorUrl ||
704
 
        fVersion)
705
 
    {
706
 
        /* <Section ovf:required="false" xsi:type="ovf:ProductSection_Type">
707
 
            <Info>Meta-information about the installed software</Info>
708
 
            <Product>VAtest</Product>
709
 
            <Vendor>SUN Microsystems</Vendor>
710
 
            <Version>10.0</Version>
711
 
            <ProductUrl>http://blogs.sun.com/VirtualGuru</ProductUrl>
712
 
            <VendorUrl>http://www.sun.com</VendorUrl>
713
 
        </Section> */
714
 
        xml::ElementNode *pelmAnnotationSection;
715
 
        if (enFormat == OVF_0_9)
716
 
        {
717
 
            // <Section ovf:required="false" xsi:type="ovf:ProductSection_Type">
718
 
            pelmAnnotationSection = pelmVirtualSystem->createChild("Section");
719
 
            pelmAnnotationSection->setAttribute("xsi:type", "ovf:ProductSection_Type");
720
 
        }
721
 
        else
722
 
            pelmAnnotationSection = pelmVirtualSystem->createChild("ProductSection");
723
 
 
724
 
        pelmAnnotationSection->createChild("Info")->addContent("Meta-information about the installed software");
725
 
        if (fProduct)
726
 
            pelmAnnotationSection->createChild("Product")->addContent(llProduct.front()->strVboxCurrent);
727
 
        if (fVendor)
728
 
            pelmAnnotationSection->createChild("Vendor")->addContent(llVendor.front()->strVboxCurrent);
729
 
        if (fVersion)
730
 
            pelmAnnotationSection->createChild("Version")->addContent(llVersion.front()->strVboxCurrent);
731
 
        if (fProductUrl)
732
 
            pelmAnnotationSection->createChild("ProductUrl")->addContent(llProductUrl.front()->strVboxCurrent);
733
 
        if (fVendorUrl)
734
 
            pelmAnnotationSection->createChild("VendorUrl")->addContent(llVendorUrl.front()->strVboxCurrent);
735
 
    }
736
 
 
737
 
    // description
738
 
    std::list<VirtualSystemDescriptionEntry*> llDescription = vsdescThis->findByType(VirtualSystemDescriptionType_Description);
739
 
    if (llDescription.size() &&
740
 
        !llDescription.front()->strVboxCurrent.isEmpty())
741
 
    {
742
 
        /*  <Section ovf:required="false" xsi:type="ovf:AnnotationSection_Type">
743
 
                <Info>A human-readable annotation</Info>
744
 
                <Annotation>Plan 9</Annotation>
745
 
            </Section> */
746
 
        xml::ElementNode *pelmAnnotationSection;
747
 
        if (enFormat == OVF_0_9)
748
 
        {
749
 
            // <Section ovf:required="false" xsi:type="ovf:AnnotationSection_Type">
750
 
            pelmAnnotationSection = pelmVirtualSystem->createChild("Section");
751
 
            pelmAnnotationSection->setAttribute("xsi:type", "ovf:AnnotationSection_Type");
752
 
        }
753
 
        else
754
 
            pelmAnnotationSection = pelmVirtualSystem->createChild("AnnotationSection");
755
 
 
756
 
        pelmAnnotationSection->createChild("Info")->addContent("A human-readable annotation");
757
 
        pelmAnnotationSection->createChild("Annotation")->addContent(llDescription.front()->strVboxCurrent);
758
 
    }
759
 
 
760
 
    // license
761
 
    std::list<VirtualSystemDescriptionEntry*> llLicense = vsdescThis->findByType(VirtualSystemDescriptionType_License);
762
 
    if (llLicense.size() &&
763
 
        !llLicense.front()->strVboxCurrent.isEmpty())
764
 
    {
765
 
        /* <EulaSection>
766
 
            <Info ovf:msgid="6">License agreement for the Virtual System.</Info>
767
 
            <License ovf:msgid="1">License terms can go in here.</License>
768
 
            </EulaSection> */
769
 
        xml::ElementNode *pelmEulaSection;
770
 
        if (enFormat == OVF_0_9)
771
 
        {
772
 
            pelmEulaSection = pelmVirtualSystem->createChild("Section");
773
 
            pelmEulaSection->setAttribute("xsi:type", "ovf:EulaSection_Type");
774
 
        }
775
 
        else
776
 
            pelmEulaSection = pelmVirtualSystem->createChild("EulaSection");
777
 
 
778
 
        pelmEulaSection->createChild("Info")->addContent("License agreement for the virtual system");
779
 
        pelmEulaSection->createChild("License")->addContent(llLicense.front()->strVboxCurrent);
780
 
    }
781
 
 
782
 
    // operating system
783
 
    std::list<VirtualSystemDescriptionEntry*> llOS = vsdescThis->findByType(VirtualSystemDescriptionType_OS);
784
 
    if (llOS.size() != 1)
785
 
        throw setError(VBOX_E_NOT_SUPPORTED,
786
 
                        tr("Missing OS type"));
787
 
    /*  <OperatingSystemSection ovf:id="82">
788
 
            <Info>Guest Operating System</Info>
789
 
            <Description>Linux 2.6.x</Description>
790
 
        </OperatingSystemSection> */
791
 
    xml::ElementNode *pelmOperatingSystemSection;
792
 
    if (enFormat == OVF_0_9)
793
 
    {
794
 
        pelmOperatingSystemSection = pelmVirtualSystem->createChild("Section");
795
 
        pelmOperatingSystemSection->setAttribute("xsi:type", "ovf:OperatingSystemSection_Type");
796
 
    }
797
 
    else
798
 
        pelmOperatingSystemSection = pelmVirtualSystem->createChild("OperatingSystemSection");
799
 
 
800
 
    pelmOperatingSystemSection->setAttribute("ovf:id", llOS.front()->strOvf);
801
 
    pelmOperatingSystemSection->createChild("Info")->addContent("The kind of installed guest operating system");
802
 
    Utf8Str strOSDesc;
803
 
    convertCIMOSType2VBoxOSType(strOSDesc, (ovf::CIMOSType_T)llOS.front()->strOvf.toInt32(), "");
804
 
    pelmOperatingSystemSection->createChild("Description")->addContent(strOSDesc);
805
 
 
806
 
    // <VirtualHardwareSection ovf:id="hw1" ovf:transport="iso">
807
 
    xml::ElementNode *pelmVirtualHardwareSection;
808
 
    if (enFormat == OVF_0_9)
809
 
    {
810
 
        // <Section xsi:type="ovf:VirtualHardwareSection_Type">
811
 
        pelmVirtualHardwareSection = pelmVirtualSystem->createChild("Section");
812
 
        pelmVirtualHardwareSection->setAttribute("xsi:type", "ovf:VirtualHardwareSection_Type");
813
 
    }
814
 
    else
815
 
        pelmVirtualHardwareSection = pelmVirtualSystem->createChild("VirtualHardwareSection");
816
 
 
817
 
    pelmVirtualHardwareSection->createChild("Info")->addContent("Virtual hardware requirements for a virtual machine");
818
 
 
819
 
    /*  <System>
820
 
            <vssd:Description>Description of the virtual hardware section.</vssd:Description>
821
 
            <vssd:ElementName>vmware</vssd:ElementName>
822
 
            <vssd:InstanceID>1</vssd:InstanceID>
823
 
            <vssd:VirtualSystemIdentifier>MyLampService</vssd:VirtualSystemIdentifier>
824
 
            <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType>
825
 
        </System> */
826
 
    xml::ElementNode *pelmSystem = pelmVirtualHardwareSection->createChild("System");
827
 
 
828
 
    pelmSystem->createChild("vssd:ElementName")->addContent("Virtual Hardware Family"); // required OVF 1.0
829
 
 
830
 
    // <vssd:InstanceId>0</vssd:InstanceId>
831
 
    if (enFormat == OVF_0_9)
832
 
        pelmSystem->createChild("vssd:InstanceId")->addContent("0");
833
 
    else // capitalization changed...
834
 
        pelmSystem->createChild("vssd:InstanceID")->addContent("0");
835
 
 
836
 
    // <vssd:VirtualSystemIdentifier>VAtest</vssd:VirtualSystemIdentifier>
837
 
    pelmSystem->createChild("vssd:VirtualSystemIdentifier")->addContent(strVMName);
838
 
    // <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType>
839
 
    const char *pcszHardware = "virtualbox-2.2";
840
 
    if (enFormat == OVF_0_9)
841
 
        // pretend to be vmware compatible then
842
 
        pcszHardware = "vmx-6";
843
 
    pelmSystem->createChild("vssd:VirtualSystemType")->addContent(pcszHardware);
844
 
 
845
 
    // loop thru all description entries twice; once to write out all
846
 
    // devices _except_ disk images, and a second time to assign the
847
 
    // disk images; this is because disk images need to reference
848
 
    // IDE controllers, and we can't know their instance IDs without
849
 
    // assigning them first
850
 
 
851
 
    uint32_t idIDEPrimaryController = 0;
852
 
    int32_t lIDEPrimaryControllerIndex = 0;
853
 
    uint32_t idIDESecondaryController = 0;
854
 
    int32_t lIDESecondaryControllerIndex = 0;
855
 
    uint32_t idSATAController = 0;
856
 
    int32_t lSATAControllerIndex = 0;
857
 
    uint32_t idSCSIController = 0;
858
 
    int32_t lSCSIControllerIndex = 0;
859
 
 
860
 
    uint32_t ulInstanceID = 1;
861
 
 
862
 
    for (size_t uLoop = 1; uLoop <= 2; ++uLoop)
863
 
    {
864
 
        int32_t lIndexThis = 0;
865
 
        list<VirtualSystemDescriptionEntry>::const_iterator itD;
866
 
        for (itD = vsdescThis->m->llDescriptions.begin();
867
 
            itD != vsdescThis->m->llDescriptions.end();
868
 
            ++itD, ++lIndexThis)
869
 
        {
870
 
            const VirtualSystemDescriptionEntry &desc = *itD;
871
 
 
872
 
            LogFlowFunc(("Loop %u: handling description entry ulIndex=%u, type=%s, strRef=%s, strOvf=%s, strVbox=%s, strExtraConfig=%s\n",
873
 
                         uLoop,
874
 
                         desc.ulIndex,
875
 
                         (  desc.type == VirtualSystemDescriptionType_HardDiskControllerIDE ? "HardDiskControllerIDE"
876
 
                          : desc.type == VirtualSystemDescriptionType_HardDiskControllerSATA ? "HardDiskControllerSATA"
877
 
                          : desc.type == VirtualSystemDescriptionType_HardDiskControllerSCSI ? "HardDiskControllerSCSI"
878
 
                          : desc.type == VirtualSystemDescriptionType_HardDiskControllerSAS ? "HardDiskControllerSAS"
879
 
                          : desc.type == VirtualSystemDescriptionType_HardDiskImage ? "HardDiskImage"
880
 
                          : Utf8StrFmt("%d", desc.type).c_str()),
881
 
                         desc.strRef.c_str(),
882
 
                         desc.strOvf.c_str(),
883
 
                         desc.strVboxCurrent.c_str(),
884
 
                         desc.strExtraConfigCurrent.c_str()));
885
 
 
886
 
            ovf::ResourceType_T type = (ovf::ResourceType_T)0;      // if this becomes != 0 then we do stuff
887
 
            Utf8Str strResourceSubType;
888
 
 
889
 
            Utf8Str strDescription;                             // results in <rasd:Description>...</rasd:Description> block
890
 
            Utf8Str strCaption;                                 // results in <rasd:Caption>...</rasd:Caption> block
891
 
 
892
 
            uint32_t ulParent = 0;
893
 
 
894
 
            int32_t lVirtualQuantity = -1;
895
 
            Utf8Str strAllocationUnits;
896
 
 
897
 
            int32_t lAddress = -1;
898
 
            int32_t lBusNumber = -1;
899
 
            int32_t lAddressOnParent = -1;
900
 
 
901
 
            int32_t lAutomaticAllocation = -1;                  // 0 means "false", 1 means "true"
902
 
            Utf8Str strConnection;                              // results in <rasd:Connection>...</rasd:Connection> block
903
 
            Utf8Str strHostResource;
904
 
 
905
 
            uint64_t uTemp;
906
 
 
907
 
            switch (desc.type)
908
 
            {
909
 
                case VirtualSystemDescriptionType_CPU:
910
 
                    /*  <Item>
911
 
                            <rasd:Caption>1 virtual CPU</rasd:Caption>
912
 
                            <rasd:Description>Number of virtual CPUs</rasd:Description>
913
 
                            <rasd:ElementName>virtual CPU</rasd:ElementName>
914
 
                            <rasd:InstanceID>1</rasd:InstanceID>
915
 
                            <rasd:ResourceType>3</rasd:ResourceType>
916
 
                            <rasd:VirtualQuantity>1</rasd:VirtualQuantity>
917
 
                        </Item> */
918
 
                    if (uLoop == 1)
919
 
                    {
920
 
                        strDescription = "Number of virtual CPUs";
921
 
                        type = ovf::ResourceType_Processor; // 3
922
 
                        desc.strVboxCurrent.toInt(uTemp);
923
 
                        lVirtualQuantity = (int32_t)uTemp;
924
 
                        strCaption = Utf8StrFmt("%d virtual CPU", lVirtualQuantity);     // without this ovftool won't eat the item
925
 
                    }
926
 
                break;
927
 
 
928
 
                case VirtualSystemDescriptionType_Memory:
929
 
                    /*  <Item>
930
 
                            <rasd:AllocationUnits>MegaBytes</rasd:AllocationUnits>
931
 
                            <rasd:Caption>256 MB of memory</rasd:Caption>
932
 
                            <rasd:Description>Memory Size</rasd:Description>
933
 
                            <rasd:ElementName>Memory</rasd:ElementName>
934
 
                            <rasd:InstanceID>2</rasd:InstanceID>
935
 
                            <rasd:ResourceType>4</rasd:ResourceType>
936
 
                            <rasd:VirtualQuantity>256</rasd:VirtualQuantity>
937
 
                        </Item> */
938
 
                    if (uLoop == 1)
939
 
                    {
940
 
                        strDescription = "Memory Size";
941
 
                        type = ovf::ResourceType_Memory; // 4
942
 
                        desc.strVboxCurrent.toInt(uTemp);
943
 
                        lVirtualQuantity = (int32_t)(uTemp / _1M);
944
 
                        strAllocationUnits = "MegaBytes";
945
 
                        strCaption = Utf8StrFmt("%d MB of memory", lVirtualQuantity);     // without this ovftool won't eat the item
946
 
                    }
947
 
                break;
948
 
 
949
 
                case VirtualSystemDescriptionType_HardDiskControllerIDE:
950
 
                    /* <Item>
951
 
                            <rasd:Caption>ideController1</rasd:Caption>
952
 
                            <rasd:Description>IDE Controller</rasd:Description>
953
 
                            <rasd:InstanceId>5</rasd:InstanceId>
954
 
                            <rasd:ResourceType>5</rasd:ResourceType>
955
 
                            <rasd:Address>1</rasd:Address>
956
 
                            <rasd:BusNumber>1</rasd:BusNumber>
957
 
                        </Item> */
958
 
                    if (uLoop == 1)
959
 
                    {
960
 
                        strDescription = "IDE Controller";
961
 
                        type = ovf::ResourceType_IDEController; // 5
962
 
                        strResourceSubType = desc.strVboxCurrent;
963
 
 
964
 
                        if (!lIDEPrimaryControllerIndex)
965
 
                        {
966
 
                            // first IDE controller:
967
 
                            strCaption = "ideController0";
968
 
                            lAddress = 0;
969
 
                            lBusNumber = 0;
970
 
                            // remember this ID
971
 
                            idIDEPrimaryController = ulInstanceID;
972
 
                            lIDEPrimaryControllerIndex = lIndexThis;
973
 
                        }
974
 
                        else
975
 
                        {
976
 
                            // second IDE controller:
977
 
                            strCaption = "ideController1";
978
 
                            lAddress = 1;
979
 
                            lBusNumber = 1;
980
 
                            // remember this ID
981
 
                            idIDESecondaryController = ulInstanceID;
982
 
                            lIDESecondaryControllerIndex = lIndexThis;
983
 
                        }
984
 
                    }
985
 
                break;
986
 
 
987
 
                case VirtualSystemDescriptionType_HardDiskControllerSATA:
988
 
                    /*  <Item>
989
 
                            <rasd:Caption>sataController0</rasd:Caption>
990
 
                            <rasd:Description>SATA Controller</rasd:Description>
991
 
                            <rasd:InstanceId>4</rasd:InstanceId>
992
 
                            <rasd:ResourceType>20</rasd:ResourceType>
993
 
                            <rasd:ResourceSubType>ahci</rasd:ResourceSubType>
994
 
                            <rasd:Address>0</rasd:Address>
995
 
                            <rasd:BusNumber>0</rasd:BusNumber>
996
 
                        </Item>
997
 
                    */
998
 
                    if (uLoop == 1)
999
 
                    {
1000
 
                        strDescription = "SATA Controller";
1001
 
                        strCaption = "sataController0";
1002
 
                        type = ovf::ResourceType_OtherStorageDevice; // 20
1003
 
                        // it seems that OVFTool always writes these two, and since we can only
1004
 
                        // have one SATA controller, we'll use this as well
1005
 
                        lAddress = 0;
1006
 
                        lBusNumber = 0;
1007
 
 
1008
 
                        if (    desc.strVboxCurrent.isEmpty()      // AHCI is the default in VirtualBox
1009
 
                             || (!desc.strVboxCurrent.compare("ahci", Utf8Str::CaseInsensitive))
1010
 
                           )
1011
 
                            strResourceSubType = "AHCI";
1012
 
                        else
1013
 
                            throw setError(VBOX_E_NOT_SUPPORTED,
1014
 
                                            tr("Invalid config string \"%s\" in SATA controller"), desc.strVboxCurrent.c_str());
1015
 
 
1016
 
                        // remember this ID
1017
 
                        idSATAController = ulInstanceID;
1018
 
                        lSATAControllerIndex = lIndexThis;
1019
 
                    }
1020
 
                break;
1021
 
 
1022
 
                case VirtualSystemDescriptionType_HardDiskControllerSCSI:
1023
 
                case VirtualSystemDescriptionType_HardDiskControllerSAS:
1024
 
                    /*  <Item>
1025
 
                            <rasd:Caption>scsiController0</rasd:Caption>
1026
 
                            <rasd:Description>SCSI Controller</rasd:Description>
1027
 
                            <rasd:InstanceId>4</rasd:InstanceId>
1028
 
                            <rasd:ResourceType>6</rasd:ResourceType>
1029
 
                            <rasd:ResourceSubType>buslogic</rasd:ResourceSubType>
1030
 
                            <rasd:Address>0</rasd:Address>
1031
 
                            <rasd:BusNumber>0</rasd:BusNumber>
1032
 
                        </Item>
1033
 
                    */
1034
 
                    if (uLoop == 1)
1035
 
                    {
1036
 
                        strDescription = "SCSI Controller";
1037
 
                        strCaption = "scsiController0";
1038
 
                        type = ovf::ResourceType_ParallelSCSIHBA; // 6
1039
 
                        // it seems that OVFTool always writes these two, and since we can only
1040
 
                        // have one SATA controller, we'll use this as well
1041
 
                        lAddress = 0;
1042
 
                        lBusNumber = 0;
1043
 
 
1044
 
                        if (    desc.strVboxCurrent.isEmpty()      // LsiLogic is the default in VirtualBox
1045
 
                             || (!desc.strVboxCurrent.compare("lsilogic", Utf8Str::CaseInsensitive))
1046
 
                            )
1047
 
                            strResourceSubType = "lsilogic";
1048
 
                        else if (!desc.strVboxCurrent.compare("buslogic", Utf8Str::CaseInsensitive))
1049
 
                            strResourceSubType = "buslogic";
1050
 
                        else if (!desc.strVboxCurrent.compare("lsilogicsas", Utf8Str::CaseInsensitive))
1051
 
                            strResourceSubType = "lsilogicsas";
1052
 
                        else
1053
 
                            throw setError(VBOX_E_NOT_SUPPORTED,
1054
 
                                            tr("Invalid config string \"%s\" in SCSI/SAS controller"), desc.strVboxCurrent.c_str());
1055
 
 
1056
 
                        // remember this ID
1057
 
                        idSCSIController = ulInstanceID;
1058
 
                        lSCSIControllerIndex = lIndexThis;
1059
 
                    }
1060
 
                break;
1061
 
 
1062
 
                case VirtualSystemDescriptionType_HardDiskImage:
1063
 
                    /*  <Item>
1064
 
                            <rasd:Caption>disk1</rasd:Caption>
1065
 
                            <rasd:InstanceId>8</rasd:InstanceId>
1066
 
                            <rasd:ResourceType>17</rasd:ResourceType>
1067
 
                            <rasd:HostResource>/disk/vmdisk1</rasd:HostResource>
1068
 
                            <rasd:Parent>4</rasd:Parent>
1069
 
                            <rasd:AddressOnParent>0</rasd:AddressOnParent>
1070
 
                        </Item> */
1071
 
                    if (uLoop == 2)
1072
 
                    {
1073
 
                        uint32_t cDisks = stack.mapDisks.size();
1074
 
                        Utf8Str strDiskID = Utf8StrFmt("vmdisk%RI32", ++cDisks);
1075
 
 
1076
 
                        strDescription = "Disk Image";
1077
 
                        strCaption = Utf8StrFmt("disk%RI32", cDisks);        // this is not used for anything else
1078
 
                        type = ovf::ResourceType_HardDisk; // 17
1079
 
 
1080
 
                        // the following references the "<Disks>" XML block
1081
 
                        strHostResource = Utf8StrFmt("/disk/%s", strDiskID.c_str());
1082
 
 
1083
 
                        // controller=<index>;channel=<c>
1084
 
                        size_t pos1 = desc.strExtraConfigCurrent.find("controller=");
1085
 
                        size_t pos2 = desc.strExtraConfigCurrent.find("channel=");
1086
 
                        int32_t lControllerIndex = -1;
1087
 
                        if (pos1 != Utf8Str::npos)
1088
 
                        {
1089
 
                            RTStrToInt32Ex(desc.strExtraConfigCurrent.c_str() + pos1 + 11, NULL, 0, &lControllerIndex);
1090
 
                            if (lControllerIndex == lIDEPrimaryControllerIndex)
1091
 
                                ulParent = idIDEPrimaryController;
1092
 
                            else if (lControllerIndex == lIDESecondaryControllerIndex)
1093
 
                                ulParent = idIDESecondaryController;
1094
 
                            else if (lControllerIndex == lSCSIControllerIndex)
1095
 
                                ulParent = idSCSIController;
1096
 
                            else if (lControllerIndex == lSATAControllerIndex)
1097
 
                                ulParent = idSATAController;
1098
 
                        }
1099
 
                        if (pos2 != Utf8Str::npos)
1100
 
                            RTStrToInt32Ex(desc.strExtraConfigCurrent.c_str() + pos2 + 8, NULL, 0, &lAddressOnParent);
1101
 
 
1102
 
                        LogFlowFunc(("HardDiskImage details: pos1=%d, pos2=%d, lControllerIndex=%d, lIDEPrimaryControllerIndex=%d, lIDESecondaryControllerIndex=%d, ulParent=%d, lAddressOnParent=%d\n",
1103
 
                                     pos1, pos2, lControllerIndex, lIDEPrimaryControllerIndex, lIDESecondaryControllerIndex, ulParent, lAddressOnParent));
1104
 
 
1105
 
                        if (    !ulParent
1106
 
                             || lAddressOnParent == -1
1107
 
                           )
1108
 
                            throw setError(VBOX_E_NOT_SUPPORTED,
1109
 
                                            tr("Missing or bad extra config string in hard disk image: \"%s\""), desc.strExtraConfigCurrent.c_str());
1110
 
 
1111
 
                        stack.mapDisks[strDiskID] = &desc;
1112
 
                    }
1113
 
                break;
1114
 
 
1115
 
                case VirtualSystemDescriptionType_Floppy:
1116
 
                    if (uLoop == 1)
1117
 
                    {
1118
 
                        strDescription = "Floppy Drive";
1119
 
                        strCaption = "floppy0";         // this is what OVFTool writes
1120
 
                        type = ovf::ResourceType_FloppyDrive; // 14
1121
 
                        lAutomaticAllocation = 0;
1122
 
                        lAddressOnParent = 0;           // this is what OVFTool writes
1123
 
                    }
1124
 
                break;
1125
 
 
1126
 
                case VirtualSystemDescriptionType_CDROM:
1127
 
                    if (uLoop == 2)
1128
 
                    {
1129
 
                        // we can't have a CD without an IDE controller
1130
 
                        if (!idIDESecondaryController)
1131
 
                            throw setError(VBOX_E_NOT_SUPPORTED,
1132
 
                                            tr("Can't have CD-ROM without secondary IDE controller"));
1133
 
 
1134
 
                        strDescription = "CD-ROM Drive";
1135
 
                        strCaption = "cdrom1";          // this is what OVFTool writes
1136
 
                        type = ovf::ResourceType_CDDrive; // 15
1137
 
                        lAutomaticAllocation = 1;
1138
 
                        ulParent = idIDESecondaryController;
1139
 
                        lAddressOnParent = 0;           // this is what OVFTool writes
1140
 
                    }
1141
 
                break;
1142
 
 
1143
 
                case VirtualSystemDescriptionType_NetworkAdapter:
1144
 
                    /* <Item>
1145
 
                            <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
1146
 
                            <rasd:Caption>Ethernet adapter on 'VM Network'</rasd:Caption>
1147
 
                            <rasd:Connection>VM Network</rasd:Connection>
1148
 
                            <rasd:ElementName>VM network</rasd:ElementName>
1149
 
                            <rasd:InstanceID>3</rasd:InstanceID>
1150
 
                            <rasd:ResourceType>10</rasd:ResourceType>
1151
 
                        </Item> */
1152
 
                    if (uLoop == 1)
1153
 
                    {
1154
 
                        lAutomaticAllocation = 1;
1155
 
                        strCaption = Utf8StrFmt("Ethernet adapter on '%s'", desc.strOvf.c_str());
1156
 
                        type = ovf::ResourceType_EthernetAdapter; // 10
1157
 
                        /* Set the hardware type to something useful.
1158
 
                            * To be compatible with vmware & others we set
1159
 
                            * PCNet32 for our PCNet types & E1000 for the
1160
 
                            * E1000 cards. */
1161
 
                        switch (desc.strVboxCurrent.toInt32())
1162
 
                        {
1163
 
                            case NetworkAdapterType_Am79C970A:
1164
 
                            case NetworkAdapterType_Am79C973: strResourceSubType = "PCNet32"; break;
1165
 
#ifdef VBOX_WITH_E1000
1166
 
                            case NetworkAdapterType_I82540EM:
1167
 
                            case NetworkAdapterType_I82545EM:
1168
 
                            case NetworkAdapterType_I82543GC: strResourceSubType = "E1000"; break;
1169
 
#endif /* VBOX_WITH_E1000 */
1170
 
                        }
1171
 
                        strConnection = desc.strOvf;
1172
 
 
1173
 
                        stack.mapNetworks[desc.strOvf] = true;
1174
 
                    }
1175
 
                break;
1176
 
 
1177
 
                case VirtualSystemDescriptionType_USBController:
1178
 
                    /*  <Item ovf:required="false">
1179
 
                            <rasd:Caption>usb</rasd:Caption>
1180
 
                            <rasd:Description>USB Controller</rasd:Description>
1181
 
                            <rasd:InstanceId>3</rasd:InstanceId>
1182
 
                            <rasd:ResourceType>23</rasd:ResourceType>
1183
 
                            <rasd:Address>0</rasd:Address>
1184
 
                            <rasd:BusNumber>0</rasd:BusNumber>
1185
 
                        </Item> */
1186
 
                    if (uLoop == 1)
1187
 
                    {
1188
 
                        strDescription = "USB Controller";
1189
 
                        strCaption = "usb";
1190
 
                        type = ovf::ResourceType_USBController; // 23
1191
 
                        lAddress = 0;                   // this is what OVFTool writes
1192
 
                        lBusNumber = 0;                 // this is what OVFTool writes
1193
 
                    }
1194
 
                break;
1195
 
 
1196
 
                case VirtualSystemDescriptionType_SoundCard:
1197
 
                /*  <Item ovf:required="false">
1198
 
                        <rasd:Caption>sound</rasd:Caption>
1199
 
                        <rasd:Description>Sound Card</rasd:Description>
1200
 
                        <rasd:InstanceId>10</rasd:InstanceId>
1201
 
                        <rasd:ResourceType>35</rasd:ResourceType>
1202
 
                        <rasd:ResourceSubType>ensoniq1371</rasd:ResourceSubType>
1203
 
                        <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
1204
 
                        <rasd:AddressOnParent>3</rasd:AddressOnParent>
1205
 
                    </Item> */
1206
 
                    if (uLoop == 1)
1207
 
                    {
1208
 
                        strDescription = "Sound Card";
1209
 
                        strCaption = "sound";
1210
 
                        type = ovf::ResourceType_SoundCard; // 35
1211
 
                        strResourceSubType = desc.strOvf;       // e.g. ensoniq1371
1212
 
                        lAutomaticAllocation = 0;
1213
 
                        lAddressOnParent = 3;               // what gives? this is what OVFTool writes
1214
 
                    }
1215
 
                break;
1216
 
            }
1217
 
 
1218
 
            if (type)
1219
 
            {
1220
 
                xml::ElementNode *pItem;
1221
 
 
1222
 
                pItem = pelmVirtualHardwareSection->createChild("Item");
1223
 
 
1224
 
                // NOTE: DO NOT CHANGE THE ORDER of these items! The OVF standards prescribes that
1225
 
                // the elements from the rasd: namespace must be sorted by letter, and VMware
1226
 
                // actually requires this as well (see public bug #6612)
1227
 
 
1228
 
                if (lAddress != -1)
1229
 
                    pItem->createChild("rasd:Address")->addContent(Utf8StrFmt("%d", lAddress));
1230
 
 
1231
 
                if (lAddressOnParent != -1)
1232
 
                    pItem->createChild("rasd:AddressOnParent")->addContent(Utf8StrFmt("%d", lAddressOnParent));
1233
 
 
1234
 
                if (!strAllocationUnits.isEmpty())
1235
 
                    pItem->createChild("rasd:AllocationUnits")->addContent(strAllocationUnits);
1236
 
 
1237
 
                if (lAutomaticAllocation != -1)
1238
 
                    pItem->createChild("rasd:AutomaticAllocation")->addContent( (lAutomaticAllocation) ? "true" : "false" );
1239
 
 
1240
 
                if (lBusNumber != -1)
1241
 
                    if (enFormat == OVF_0_9) // BusNumber is invalid OVF 1.0 so only write it in 0.9 mode for OVFTool compatibility
1242
 
                        pItem->createChild("rasd:BusNumber")->addContent(Utf8StrFmt("%d", lBusNumber));
1243
 
 
1244
 
                if (!strCaption.isEmpty())
1245
 
                    pItem->createChild("rasd:Caption")->addContent(strCaption);
1246
 
 
1247
 
                if (!strConnection.isEmpty())
1248
 
                    pItem->createChild("rasd:Connection")->addContent(strConnection);
1249
 
 
1250
 
                if (!strDescription.isEmpty())
1251
 
                    pItem->createChild("rasd:Description")->addContent(strDescription);
1252
 
 
1253
 
                if (!strCaption.isEmpty())
1254
 
                    if (enFormat == OVF_1_0)
1255
 
                        pItem->createChild("rasd:ElementName")->addContent(strCaption);
1256
 
 
1257
 
                if (!strHostResource.isEmpty())
1258
 
                    pItem->createChild("rasd:HostResource")->addContent(strHostResource);
1259
 
 
1260
 
                // <rasd:InstanceID>1</rasd:InstanceID>
1261
 
                xml::ElementNode *pelmInstanceID;
1262
 
                if (enFormat == OVF_0_9)
1263
 
                    pelmInstanceID = pItem->createChild("rasd:InstanceId");
1264
 
                else
1265
 
                    pelmInstanceID = pItem->createChild("rasd:InstanceID");      // capitalization changed...
1266
 
                pelmInstanceID->addContent(Utf8StrFmt("%d", ulInstanceID++));
1267
 
 
1268
 
                if (ulParent)
1269
 
                    pItem->createChild("rasd:Parent")->addContent(Utf8StrFmt("%d", ulParent));
1270
 
 
1271
 
                if (!strResourceSubType.isEmpty())
1272
 
                    pItem->createChild("rasd:ResourceSubType")->addContent(strResourceSubType);
1273
 
 
1274
 
                // <rasd:ResourceType>3</rasd:ResourceType>
1275
 
                pItem->createChild("rasd:ResourceType")->addContent(Utf8StrFmt("%d", type));
1276
 
 
1277
 
                // <rasd:VirtualQuantity>1</rasd:VirtualQuantity>
1278
 
                if (lVirtualQuantity != -1)
1279
 
                    pItem->createChild("rasd:VirtualQuantity")->addContent(Utf8StrFmt("%d", lVirtualQuantity));
1280
 
            }
1281
 
        }
1282
 
    } // for (size_t uLoop = 1; uLoop <= 2; ++uLoop)
1283
 
 
1284
 
    // now that we're done with the official OVF <Item> tags under <VirtualSystem>, write out VirtualBox XML
1285
 
    // under the vbox: namespace
1286
 
    xml::ElementNode *pelmVBoxMachine = pelmVirtualSystem->createChild("vbox:Machine");
1287
 
    // ovf:required="false" tells other OVF parsers that they can ignore this thing
1288
 
    pelmVBoxMachine->setAttribute("ovf:required", "false");
1289
 
    // ovf:Info element is required or VMware will bail out on the vbox:Machine element
1290
 
    pelmVBoxMachine->createChild("ovf:Info")->addContent("Complete VirtualBox machine configuration in VirtualBox format");
1291
 
 
1292
 
    // create an empty machine config
1293
 
    settings::MachineConfigFile *pConfig = new settings::MachineConfigFile(NULL);
1294
 
 
1295
 
    try
1296
 
    {
1297
 
        AutoWriteLock machineLock(vsdescThis->m->pMachine COMMA_LOCKVAL_SRC_POS);
1298
 
        // fill the machine config
1299
 
        vsdescThis->m->pMachine->copyMachineDataToSettings(*pConfig);
1300
 
        // write the machine config to the vbox:Machine element
1301
 
        pConfig->buildMachineXML(*pelmVBoxMachine,
1302
 
                                   settings::MachineConfigFile::BuildMachineXML_WriteVboxVersionAttribute
1303
 
                                 | settings::MachineConfigFile::BuildMachineXML_SkipRemovableMedia,
1304
 
                                        // but not BuildMachineXML_IncludeSnapshots
1305
 
                                 pllElementsWithUuidAttributes);
1306
 
        delete pConfig;
1307
 
    }
1308
 
    catch (...)
1309
 
    {
1310
 
        delete pConfig;
1311
 
        throw;
1312
 
    }
1313
 
}
1314
 
 
1315
 
/**
1316
 
 * Actual worker code for writing out OVF to disk. This is called from Appliance::taskThreadWriteOVF()
1317
 
 * and therefore runs on the OVF write worker thread. This runs in two contexts:
1318
 
 *
1319
 
 * 1) in a first worker thread; in that case, Appliance::Write() called Appliance::writeImpl();
1320
 
 *
1321
 
 * 2) in a second worker thread; in that case, Appliance::Write() called Appliance::writeImpl(), which
1322
 
 *    called Appliance::writeS3(), which called Appliance::writeImpl(), which then called this. In other
1323
 
 *    words, to write to the cloud, the first worker thread first starts a second worker thread to create
1324
 
 *    temporary files and then uploads them to the S3 cloud server.
1325
 
 *
1326
 
 * @param pTask
1327
 
 * @return
1328
 
 */
1329
 
HRESULT Appliance::writeFS(const LocationInfo &locInfo, const OVFFormat enFormat, ComObjPtr<Progress> &pProgress)
1330
 
{
1331
 
    LogFlowFunc(("ENTER appliance %p\n", this));
1332
 
 
1333
 
    AutoCaller autoCaller(this);
1334
 
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
1335
 
 
1336
 
    HRESULT rc = S_OK;
1337
 
 
1338
 
    try
1339
 
    {
1340
 
        AutoMultiWriteLock2 multiLock(&mVirtualBox->getMediaTreeLockHandle(), this->lockHandle() COMMA_LOCKVAL_SRC_POS);
1341
 
 
1342
 
        xml::Document doc;
1343
 
        xml::ElementNode *pelmRoot = doc.createRootElement("Envelope");
1344
 
 
1345
 
        pelmRoot->setAttribute("ovf:version", (enFormat == OVF_1_0) ? "1.0" : "0.9");
1346
 
        pelmRoot->setAttribute("xml:lang", "en-US");
1347
 
 
1348
 
        Utf8Str strNamespace = (enFormat == OVF_0_9)
1349
 
            ? "http://www.vmware.com/schema/ovf/1/envelope"     // 0.9
1350
 
            : "http://schemas.dmtf.org/ovf/envelope/1";         // 1.0
1351
 
        pelmRoot->setAttribute("xmlns", strNamespace);
1352
 
        pelmRoot->setAttribute("xmlns:ovf", strNamespace);
1353
 
 
1354
 
//         pelmRoot->setAttribute("xmlns:ovfstr", "http://schema.dmtf.org/ovf/strings/1");
1355
 
        pelmRoot->setAttribute("xmlns:rasd", "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData");
1356
 
        pelmRoot->setAttribute("xmlns:vssd", "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData");
1357
 
        pelmRoot->setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
1358
 
        pelmRoot->setAttribute("xmlns:vbox", "http://www.virtualbox.org/ovf/machine");
1359
 
//         pelmRoot->setAttribute("xsi:schemaLocation", "http://schemas.dmtf.org/ovf/envelope/1 ../ovf-envelope.xsd");
1360
 
 
1361
 
        // <Envelope>/<References>
1362
 
        xml::ElementNode *pelmReferences = pelmRoot->createChild("References");     // 0.9 and 1.0
1363
 
 
1364
 
        /* <Envelope>/<DiskSection>:
1365
 
            <DiskSection>
1366
 
                <Info>List of the virtual disks used in the package</Info>
1367
 
                <Disk ovf:capacity="4294967296" ovf:diskId="lamp" ovf:format="..." ovf:populatedSize="1924967692"/>
1368
 
            </DiskSection> */
1369
 
        xml::ElementNode *pelmDiskSection;
1370
 
        if (enFormat == OVF_0_9)
1371
 
        {
1372
 
            // <Section xsi:type="ovf:DiskSection_Type">
1373
 
            pelmDiskSection = pelmRoot->createChild("Section");
1374
 
            pelmDiskSection->setAttribute("xsi:type", "ovf:DiskSection_Type");
1375
 
        }
1376
 
        else
1377
 
            pelmDiskSection = pelmRoot->createChild("DiskSection");
1378
 
 
1379
 
        xml::ElementNode *pelmDiskSectionInfo = pelmDiskSection->createChild("Info");
1380
 
        pelmDiskSectionInfo->addContent("List of the virtual disks used in the package");
1381
 
 
1382
 
        // the XML stack contains two maps for disks and networks, which allows us to
1383
 
        // a) have a list of unique disk names (to make sure the same disk name is only added once)
1384
 
        // and b) keep a list of all networks
1385
 
        XMLStack stack;
1386
 
 
1387
 
        /* <Envelope>/<NetworkSection>:
1388
 
            <NetworkSection>
1389
 
                <Info>Logical networks used in the package</Info>
1390
 
                <Network ovf:name="VM Network">
1391
 
                    <Description>The network that the LAMP Service will be available on</Description>
1392
 
                </Network>
1393
 
            </NetworkSection> */
1394
 
        xml::ElementNode *pelmNetworkSection;
1395
 
        if (enFormat == OVF_0_9)
1396
 
        {
1397
 
            // <Section xsi:type="ovf:NetworkSection_Type">
1398
 
            pelmNetworkSection = pelmRoot->createChild("Section");
1399
 
            pelmNetworkSection->setAttribute("xsi:type", "ovf:NetworkSection_Type");
1400
 
        }
1401
 
        else
1402
 
            pelmNetworkSection = pelmRoot->createChild("NetworkSection");
1403
 
 
1404
 
        xml::ElementNode *pelmNetworkSectionInfo = pelmNetworkSection->createChild("Info");
1405
 
        pelmNetworkSectionInfo->addContent("Logical networks used in the package");
1406
 
 
1407
 
        // and here come the virtual systems:
1408
 
 
1409
 
        // This can take a very long time so leave the locks; in particular, we have the media tree
1410
 
        // lock which Medium::CloneTo() will request, and that would deadlock. Instead, protect
1411
 
        // the appliance by resetting its state so we can safely leave the lock
1412
 
        m->state = Data::ApplianceExporting;
1413
 
        multiLock.release();
1414
 
 
1415
 
        // write a collection if we have more than one virtual system _and_ we're
1416
 
        // writing OVF 1.0; otherwise fail since ovftool can't import more than
1417
 
        // one machine, it seems
1418
 
        xml::ElementNode *pelmToAddVirtualSystemsTo;
1419
 
        if (m->virtualSystemDescriptions.size() > 1)
1420
 
        {
1421
 
            if (enFormat == OVF_0_9)
1422
 
                throw setError(VBOX_E_FILE_ERROR,
1423
 
                               tr("Cannot export more than one virtual system with OVF 0.9, use OVF 1.0"));
1424
 
 
1425
 
            pelmToAddVirtualSystemsTo = pelmRoot->createChild("VirtualSystemCollection");
1426
 
            pelmToAddVirtualSystemsTo->setAttribute("ovf:name", "ExportedVirtualBoxMachines");      // whatever
1427
 
        }
1428
 
        else
1429
 
            pelmToAddVirtualSystemsTo = pelmRoot;       // add virtual system directly under root element
1430
 
 
1431
 
        // this list receives pointers to the XML elements in the machine XML which
1432
 
        // might have UUIDs that need fixing after we know the UUIDs of the exported images
1433
 
        std::list<xml::ElementNode*> llElementsWithUuidAttributes;
1434
 
 
1435
 
        list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;
1436
 
        /* Iterate throughs all virtual systems of that appliance */
1437
 
        for (it = m->virtualSystemDescriptions.begin();
1438
 
             it != m->virtualSystemDescriptions.end();
1439
 
             ++it)
1440
 
        {
1441
 
            ComObjPtr<VirtualSystemDescription> vsdescThis = *it;
1442
 
            buildXMLForOneVirtualSystem(*pelmToAddVirtualSystemsTo,
1443
 
                                        &llElementsWithUuidAttributes,
1444
 
                                        vsdescThis,
1445
 
                                        enFormat,
1446
 
                                        stack);         // disks and networks stack
1447
 
        }
1448
 
 
1449
 
        // now, fill in the network section we set up empty above according
1450
 
        // to the networks we found with the hardware items
1451
 
        map<Utf8Str, bool>::const_iterator itN;
1452
 
        for (itN = stack.mapNetworks.begin();
1453
 
             itN != stack.mapNetworks.end();
1454
 
             ++itN)
1455
 
        {
1456
 
            const Utf8Str &strNetwork = itN->first;
1457
 
            xml::ElementNode *pelmNetwork = pelmNetworkSection->createChild("Network");
1458
 
            pelmNetwork->setAttribute("ovf:name", strNetwork.c_str());
1459
 
            pelmNetwork->createChild("Description")->addContent("Logical network used by this appliance.");
1460
 
        }
1461
 
 
1462
 
        // Finally, write out the disks!
1463
 
 
1464
 
        list<Utf8Str> diskList;
1465
 
        map<Utf8Str, const VirtualSystemDescriptionEntry*>::const_iterator itS;
1466
 
        uint32_t ulFile = 1;
1467
 
        for (itS = stack.mapDisks.begin();
1468
 
             itS != stack.mapDisks.end();
1469
 
             ++itS)
1470
 
        {
1471
 
            const Utf8Str &strDiskID = itS->first;
1472
 
            const VirtualSystemDescriptionEntry *pDiskEntry = itS->second;
1473
 
 
1474
 
            // source path: where the VBox image is
1475
 
            const Utf8Str &strSrcFilePath = pDiskEntry->strVboxCurrent;
1476
 
            Bstr bstrSrcFilePath(strSrcFilePath);
1477
 
            if (!RTPathExists(strSrcFilePath.c_str()))
1478
 
                /* This isn't allowed */
1479
 
                throw setError(VBOX_E_FILE_ERROR,
1480
 
                               tr("Source virtual disk image file '%s' doesn't exist"),
1481
 
                               strSrcFilePath.c_str());
1482
 
 
1483
 
            // clone the disk:
1484
 
            ComPtr<IMedium> pSourceDisk;
1485
 
            ComPtr<IMedium> pTargetDisk;
1486
 
            ComPtr<IProgress> pProgress2;
1487
 
 
1488
 
            Log(("Finding source disk \"%ls\"\n", bstrSrcFilePath.raw()));
1489
 
            rc = mVirtualBox->FindHardDisk(bstrSrcFilePath, pSourceDisk.asOutParam());
1490
 
            if (FAILED(rc)) throw rc;
1491
 
 
1492
 
            Bstr uuidSource;
1493
 
            rc = pSourceDisk->COMGETTER(Id)(uuidSource.asOutParam());
1494
 
            if (FAILED(rc)) throw rc;
1495
 
            Guid guidSource(uuidSource);
1496
 
 
1497
 
            // output filename
1498
 
            const Utf8Str &strTargetFileNameOnly = pDiskEntry->strOvf;
1499
 
            // target path needs to be composed from where the output OVF is
1500
 
            Utf8Str strTargetFilePath(locInfo.strPath);
1501
 
            strTargetFilePath.stripFilename();
1502
 
            strTargetFilePath.append("/");
1503
 
            strTargetFilePath.append(strTargetFileNameOnly);
1504
 
 
1505
 
            // We are always exporting to VMDK stream optimized for now
1506
 
            Bstr bstrSrcFormat = L"VMDK";
1507
 
 
1508
 
            // create a new hard disk interface for the destination disk image
1509
 
            Log(("Creating target disk \"%s\"\n", strTargetFilePath.raw()));
1510
 
            rc = mVirtualBox->CreateHardDisk(bstrSrcFormat, Bstr(strTargetFilePath), pTargetDisk.asOutParam());
1511
 
            if (FAILED(rc)) throw rc;
1512
 
 
1513
 
            // the target disk is now registered and needs to be removed again,
1514
 
            // both after successful cloning or if anything goes bad!
1515
 
            try
1516
 
            {
1517
 
                // create a flat copy of the source disk image
1518
 
                rc = pSourceDisk->CloneTo(pTargetDisk, MediumVariant_VmdkStreamOptimized, NULL, pProgress2.asOutParam());
1519
 
                if (FAILED(rc)) throw rc;
1520
 
 
1521
 
                // advance to the next operation
1522
 
                pProgress->SetNextOperation(BstrFmt(tr("Exporting to disk image '%s'"), strTargetFilePath.c_str()),
1523
 
                                            pDiskEntry->ulSizeMB);     // operation's weight, as set up with the IProgress originally);
1524
 
 
1525
 
                // now wait for the background disk operation to complete; this throws HRESULTs on error
1526
 
                waitForAsyncProgress(pProgress, pProgress2);
1527
 
            }
1528
 
            catch (HRESULT rc3)
1529
 
            {
1530
 
                // upon error after registering, close the disk or
1531
 
                // it'll stick in the registry forever
1532
 
                pTargetDisk->Close();
1533
 
                throw rc3;
1534
 
            }
1535
 
            diskList.push_back(strTargetFilePath);
1536
 
 
1537
 
            // we need the following for the XML
1538
 
            ULONG64 cbFile = 0;        // actual file size
1539
 
            rc = pTargetDisk->COMGETTER(Size)(&cbFile);
1540
 
            if (FAILED(rc)) throw rc;
1541
 
 
1542
 
            ULONG64 cbCapacity = 0;     // size reported to guest
1543
 
            rc = pTargetDisk->COMGETTER(LogicalSize)(&cbCapacity);
1544
 
            if (FAILED(rc)) throw rc;
1545
 
            // capacity is reported in megabytes, so...
1546
 
            cbCapacity *= _1M;
1547
 
 
1548
 
            Bstr uuidTarget;
1549
 
            rc = pTargetDisk->COMGETTER(Id)(uuidTarget.asOutParam());
1550
 
            if (FAILED(rc)) throw rc;
1551
 
            Guid guidTarget(uuidTarget);
1552
 
 
1553
 
            // upon success, close the disk as well
1554
 
            rc = pTargetDisk->Close();
1555
 
            if (FAILED(rc)) throw rc;
1556
 
 
1557
 
            // now handle the XML for the disk:
1558
 
            Utf8StrFmt strFileRef("file%RI32", ulFile++);
1559
 
            // <File ovf:href="WindowsXpProfessional-disk1.vmdk" ovf:id="file1" ovf:size="1710381056"/>
1560
 
            xml::ElementNode *pelmFile = pelmReferences->createChild("File");
1561
 
            pelmFile->setAttribute("ovf:href", strTargetFileNameOnly);
1562
 
            pelmFile->setAttribute("ovf:id", strFileRef);
1563
 
            pelmFile->setAttribute("ovf:size", Utf8StrFmt("%RI64", cbFile).c_str());
1564
 
 
1565
 
            // add disk to XML Disks section
1566
 
            // <Disk ovf:capacity="8589934592" ovf:diskId="vmdisk1" ovf:fileRef="file1" ovf:format="..."/>
1567
 
            xml::ElementNode *pelmDisk = pelmDiskSection->createChild("Disk");
1568
 
            pelmDisk->setAttribute("ovf:capacity", Utf8StrFmt("%RI64", cbCapacity).c_str());
1569
 
            pelmDisk->setAttribute("ovf:diskId", strDiskID);
1570
 
            pelmDisk->setAttribute("ovf:fileRef", strFileRef);
1571
 
            pelmDisk->setAttribute("ovf:format",
1572
 
                    (enFormat == OVF_0_9)
1573
 
                        ?  "http://www.vmware.com/specifications/vmdk.html#sparse"      // must be sparse or ovftool chokes
1574
 
                        :  "http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized"
1575
 
                                                                                    // correct string as communicated to us by VMware (public bug #6612)
1576
 
                                  );
1577
 
 
1578
 
            // add the UUID of the newly created target image to the OVF disk element, but in the
1579
 
            // vbox: namespace since it's not part of the standard
1580
 
            pelmDisk->setAttribute("vbox:uuid", Utf8StrFmt("%RTuuid", guidTarget.raw()).c_str());
1581
 
 
1582
 
            // now, we might have other XML elements from vbox:Machine pointing to this image,
1583
 
            // but those would refer to the UUID of the _source_ image (which we created the
1584
 
            // export image from); those UUIDs need to be fixed to the export image
1585
 
            Utf8Str strGuidSourceCurly = guidSource.toStringCurly();
1586
 
            for (std::list<xml::ElementNode*>::iterator eit = llElementsWithUuidAttributes.begin();
1587
 
                 eit != llElementsWithUuidAttributes.end();
1588
 
                 ++eit)
1589
 
            {
1590
 
                xml::ElementNode *pelmImage = *eit;
1591
 
                Utf8Str strUUID;
1592
 
                pelmImage->getAttributeValue("uuid", strUUID);
1593
 
                if (strUUID == strGuidSourceCurly)
1594
 
                    // overwrite existing uuid attribute
1595
 
                    pelmImage->setAttribute("uuid", guidTarget.toStringCurly());
1596
 
            }
1597
 
        }
1598
 
 
1599
 
        // now go write the XML
1600
 
        xml::XmlFileWriter writer(doc);
1601
 
        writer.write(locInfo.strPath.c_str(), false /*fSafe*/);
1602
 
 
1603
 
#if 0 // VBox 3.2.10: disable manifest writing until it's actually usable
1604
 
        // Create & write the manifest file
1605
 
        Utf8Str strMfFile = manifestFileName(locInfo.strPath.c_str());
1606
 
        const char *pcszManifestFileOnly = RTPathFilename(strMfFile.c_str());
1607
 
        pProgress->SetNextOperation(BstrFmt(tr("Creating manifest file '%s'"), pcszManifestFileOnly),
1608
 
                                    m->ulWeightForManifestOperation);     // operation's weight, as set up with the IProgress originally);
1609
 
 
1610
 
        const char** ppManifestFiles = (const char**)RTMemAlloc(sizeof(char*)*diskList.size() + 1);
1611
 
        ppManifestFiles[0] = locInfo.strPath.c_str();
1612
 
        list<Utf8Str>::const_iterator it1;
1613
 
        size_t i = 1;
1614
 
        for (it1 = diskList.begin();
1615
 
             it1 != diskList.end();
1616
 
             ++it1, ++i)
1617
 
            ppManifestFiles[i] = (*it1).c_str();
1618
 
        int vrc = RTManifestWriteFiles(strMfFile.c_str(), ppManifestFiles, diskList.size()+1, NULL, NULL);
1619
 
        RTMemFree(ppManifestFiles);
1620
 
        if (RT_FAILURE(vrc))
1621
 
            throw setError(VBOX_E_FILE_ERROR,
1622
 
                           tr("Could not create manifest file '%s' (%Rrc)"),
1623
 
                           pcszManifestFileOnly, vrc);
1624
 
#endif
1625
 
    }
1626
 
    catch(xml::Error &x)
1627
 
    {
1628
 
        rc = setError(VBOX_E_FILE_ERROR,
1629
 
                      x.what());
1630
 
    }
1631
 
    catch(HRESULT aRC)
1632
 
    {
1633
 
        rc = aRC;
1634
 
    }
1635
 
 
1636
 
    AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);
1637
 
    // reset the state so others can call methods again
1638
 
    m->state = Data::ApplianceIdle;
1639
 
 
1640
 
    LogFlowFunc(("rc=%Rhrc\n", rc));
1641
 
    LogFlowFuncLeave();
1642
 
 
1643
 
    return rc;
1644
 
}
1645
 
 
1646
 
/**
1647
 
 * Worker code for writing out OVF to the cloud. This is called from Appliance::taskThreadWriteOVF()
1648
 
 * in S3 mode and therefore runs on the OVF write worker thread. This then starts a second worker
1649
 
 * thread to create temporary files (see Appliance::writeFS()).
1650
 
 *
1651
 
 * @param pTask
1652
 
 * @return
1653
 
 */
1654
 
HRESULT Appliance::writeS3(TaskOVF *pTask)
1655
 
{
1656
 
    LogFlowFuncEnter();
1657
 
    LogFlowFunc(("Appliance %p\n", this));
1658
 
 
1659
 
    AutoCaller autoCaller(this);
1660
 
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
1661
 
 
1662
 
    HRESULT rc = S_OK;
1663
 
 
1664
 
    AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);
1665
 
 
1666
 
    int vrc = VINF_SUCCESS;
1667
 
    RTS3 hS3 = NIL_RTS3;
1668
 
    char szOSTmpDir[RTPATH_MAX];
1669
 
    RTPathTemp(szOSTmpDir, sizeof(szOSTmpDir));
1670
 
    /* The template for the temporary directory created below */
1671
 
    char *pszTmpDir;
1672
 
    RTStrAPrintf(&pszTmpDir, "%s"RTPATH_SLASH_STR"vbox-ovf-XXXXXX", szOSTmpDir);
1673
 
    list< pair<Utf8Str, ULONG> > filesList;
1674
 
 
1675
 
    // todo:
1676
 
    // - usable error codes
1677
 
    // - seems snapshot filenames are problematic {uuid}.vdi
1678
 
    try
1679
 
    {
1680
 
        /* Extract the bucket */
1681
 
        Utf8Str tmpPath = pTask->locInfo.strPath;
1682
 
        Utf8Str bucket;
1683
 
        parseBucket(tmpPath, bucket);
1684
 
 
1685
 
        /* We need a temporary directory which we can put the OVF file & all
1686
 
         * disk images in */
1687
 
        vrc = RTDirCreateTemp(pszTmpDir);
1688
 
        if (RT_FAILURE(vrc))
1689
 
            throw setError(VBOX_E_FILE_ERROR,
1690
 
                           tr("Cannot create temporary directory '%s'"), pszTmpDir);
1691
 
 
1692
 
        /* The temporary name of the target OVF file */
1693
 
        Utf8StrFmt strTmpOvf("%s/%s", pszTmpDir, RTPathFilename(tmpPath.c_str()));
1694
 
 
1695
 
        /* Prepare the temporary writing of the OVF */
1696
 
        ComObjPtr<Progress> progress;
1697
 
        /* Create a temporary file based location info for the sub task */
1698
 
        LocationInfo li;
1699
 
        li.strPath = strTmpOvf;
1700
 
        rc = writeImpl(pTask->enFormat, li, progress);
1701
 
        if (FAILED(rc)) throw rc;
1702
 
 
1703
 
        /* Unlock the appliance for the writing thread */
1704
 
        appLock.release();
1705
 
        /* Wait until the writing is done, but report the progress back to the
1706
 
           caller */
1707
 
        ComPtr<IProgress> progressInt(progress);
1708
 
        waitForAsyncProgress(pTask->pProgress, progressInt); /* Any errors will be thrown */
1709
 
 
1710
 
        /* Again lock the appliance for the next steps */
1711
 
        appLock.acquire();
1712
 
 
1713
 
        vrc = RTPathExists(strTmpOvf.c_str()); /* Paranoid check */
1714
 
        if (RT_FAILURE(vrc))
1715
 
            throw setError(VBOX_E_FILE_ERROR,
1716
 
                           tr("Cannot find source file '%s'"), strTmpOvf.c_str());
1717
 
        /* Add the OVF file */
1718
 
        filesList.push_back(pair<Utf8Str, ULONG>(strTmpOvf, m->ulWeightForXmlOperation)); /* Use 1% of the total for the OVF file upload */
1719
 
        Utf8Str strMfFile = manifestFileName(strTmpOvf);
1720
 
        filesList.push_back(pair<Utf8Str, ULONG>(strMfFile , m->ulWeightForXmlOperation)); /* Use 1% of the total for the manifest file upload */
1721
 
 
1722
 
        /* Now add every disks of every virtual system */
1723
 
        list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;
1724
 
        for (it = m->virtualSystemDescriptions.begin();
1725
 
             it != m->virtualSystemDescriptions.end();
1726
 
             ++it)
1727
 
        {
1728
 
            ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);
1729
 
            std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
1730
 
            std::list<VirtualSystemDescriptionEntry*>::const_iterator itH;
1731
 
            for (itH = avsdeHDs.begin();
1732
 
                 itH != avsdeHDs.end();
1733
 
                 ++itH)
1734
 
            {
1735
 
                const Utf8Str &strTargetFileNameOnly = (*itH)->strOvf;
1736
 
                /* Target path needs to be composed from where the output OVF is */
1737
 
                Utf8Str strTargetFilePath(strTmpOvf);
1738
 
                strTargetFilePath.stripFilename();
1739
 
                strTargetFilePath.append("/");
1740
 
                strTargetFilePath.append(strTargetFileNameOnly);
1741
 
                vrc = RTPathExists(strTargetFilePath.c_str()); /* Paranoid check */
1742
 
                if (RT_FAILURE(vrc))
1743
 
                    throw setError(VBOX_E_FILE_ERROR,
1744
 
                                   tr("Cannot find source file '%s'"), strTargetFilePath.c_str());
1745
 
                filesList.push_back(pair<Utf8Str, ULONG>(strTargetFilePath, (*itH)->ulSizeMB));
1746
 
            }
1747
 
        }
1748
 
        /* Next we have to upload the OVF & all disk images */
1749
 
        vrc = RTS3Create(&hS3, pTask->locInfo.strUsername.c_str(), pTask->locInfo.strPassword.c_str(), pTask->locInfo.strHostname.c_str(), "virtualbox-agent/"VBOX_VERSION_STRING);
1750
 
        if (RT_FAILURE(vrc))
1751
 
            throw setError(VBOX_E_IPRT_ERROR,
1752
 
                           tr("Cannot create S3 service handler"));
1753
 
        RTS3SetProgressCallback(hS3, pTask->updateProgress, &pTask);
1754
 
 
1755
 
        /* Upload all files */
1756
 
        for (list< pair<Utf8Str, ULONG> >::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1)
1757
 
        {
1758
 
            const pair<Utf8Str, ULONG> &s = (*it1);
1759
 
            char *pszFilename = RTPathFilename(s.first.c_str());
1760
 
            /* Advance to the next operation */
1761
 
            pTask->pProgress->SetNextOperation(BstrFmt(tr("Uploading file '%s'"), pszFilename), s.second);
1762
 
            vrc = RTS3PutKey(hS3, bucket.c_str(), pszFilename, s.first.c_str());
1763
 
            if (RT_FAILURE(vrc))
1764
 
            {
1765
 
                if (vrc == VERR_S3_CANCELED)
1766
 
                    break;
1767
 
                else if (vrc == VERR_S3_ACCESS_DENIED)
1768
 
                    throw setError(E_ACCESSDENIED,
1769
 
                                   tr("Cannot upload file '%s' to S3 storage server (Access denied). Make sure that your credentials are right. Also check that your host clock is properly synced"), pszFilename);
1770
 
                else if (vrc == VERR_S3_NOT_FOUND)
1771
 
                    throw setError(VBOX_E_FILE_ERROR,
1772
 
                                   tr("Cannot upload file '%s' to S3 storage server (File not found)"), pszFilename);
1773
 
                else
1774
 
                    throw setError(VBOX_E_IPRT_ERROR,
1775
 
                                   tr("Cannot upload file '%s' to S3 storage server (%Rrc)"), pszFilename, vrc);
1776
 
            }
1777
 
        }
1778
 
    }
1779
 
    catch(HRESULT aRC)
1780
 
    {
1781
 
        rc = aRC;
1782
 
    }
1783
 
    /* Cleanup */
1784
 
    RTS3Destroy(hS3);
1785
 
    /* Delete all files which where temporary created */
1786
 
    for (list< pair<Utf8Str, ULONG> >::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1)
1787
 
    {
1788
 
        const char *pszFilePath = (*it1).first.c_str();
1789
 
        if (RTPathExists(pszFilePath))
1790
 
        {
1791
 
            vrc = RTFileDelete(pszFilePath);
1792
 
            if (RT_FAILURE(vrc))
1793
 
                rc = setError(VBOX_E_FILE_ERROR,
1794
 
                              tr("Cannot delete file '%s' (%Rrc)"), pszFilePath, vrc);
1795
 
        }
1796
 
    }
1797
 
    /* Delete the temporary directory */
1798
 
    if (RTPathExists(pszTmpDir))
1799
 
    {
1800
 
        vrc = RTDirRemove(pszTmpDir);
1801
 
        if (RT_FAILURE(vrc))
1802
 
            rc = setError(VBOX_E_FILE_ERROR,
1803
 
                          tr("Cannot delete temporary directory '%s' (%Rrc)"), pszTmpDir, vrc);
1804
 
    }
1805
 
    if (pszTmpDir)
1806
 
        RTStrFree(pszTmpDir);
1807
 
 
1808
 
    LogFlowFunc(("rc=%Rhrc\n", rc));
1809
 
    LogFlowFuncLeave();
1810
 
 
1811
 
    return rc;
1812
 
}
1813