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

« back to all changes in this revision

Viewing changes to src/VBox/Main/xml/ovfreader.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Felix Geyer
  • Date: 2009-12-18 16:44:29 UTC
  • mfrom: (0.3.3 upstream) (0.4.6 squeeze)
  • Revision ID: james.westby@ubuntu.com-20091218164429-jd34ccexpv5na11a
Tags: 3.1.2-dfsg-1ubuntu1
* Merge from Debian unstable (LP: #498219), remaining changes:
  - Disable update action
    - debian/patches/u01-disable-update-action.dpatch
  - VirtualBox should go in Accessories, not in System tools (LP: #288590)
    - debian/virtualbox-ose-qt.files/virtualbox-ose.desktop
  - Add Apport hook
    - debian/virtualbox-ose.files/source_virtualbox-ose.py
    - debian/virtualbox-ose.install
  - Add Launchpad integration
    - debian/control
    - debian/lpi-bug.xpm
    - debian/patches/u02-lp-integration.dpatch
* Fixes the following bugs:
  - Kernel module fails to build with Linux >= 2.6.32 (LP: #474625)
  - X.Org drivers need to be rebuilt against X-Server 1.7 (LP: #495935)
  - The *-source packages try to build the kernel modules even though the
    kernel headers aren't available (LP: #473334)
* Replace *-source packages with transitional packages for *-dkms.
* Adapt u01-disable-update-action.dpatch and u02-lp-integration.dpatch for
  new upstream version.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $Id: ovfreader.cpp $ */
 
2
/** @file
 
3
 *
 
4
 * OVF reader declarations. Depends only on IPRT, including the iprt::MiniString
 
5
 * and IPRT XML classes.
 
6
 */
 
7
 
 
8
/*
 
9
 * Copyright (C) 2008-2009 Sun Microsystems, Inc.
 
10
 *
 
11
 * This file is part of VirtualBox Open Source Edition (OSE), as
 
12
 * available from http://www.virtualbox.org. This file is free software;
 
13
 * you can redistribute it and/or modify it under the terms of the GNU
 
14
 * General Public License (GPL) as published by the Free Software
 
15
 * Foundation, in version 2 as it comes in the "COPYING" file of the
 
16
 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
 
17
 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
 
18
 *
 
19
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
 
20
 * Clara, CA 95054 USA or visit http://www.sun.com if you need
 
21
 * additional information or have any questions.
 
22
 */
 
23
 
 
24
#include "ovfreader.h"
 
25
 
 
26
using namespace std;
 
27
using namespace iprt;
 
28
 
 
29
////////////////////////////////////////////////////////////////////////////////
 
30
//
 
31
// OVF reader implemenation
 
32
//
 
33
////////////////////////////////////////////////////////////////////////////////
 
34
 
 
35
OVFReader::OVFReader(const MiniString &path)
 
36
    : m_strPath(path)
 
37
{
 
38
    xml::XmlFileParser parser;
 
39
    xml::Document doc;
 
40
    parser.read(m_strPath,
 
41
                doc);
 
42
 
 
43
    const xml::ElementNode *pRootElem = doc.getRootElement();
 
44
    if (pRootElem && strcmp(pRootElem->getName(), "Envelope"))
 
45
        throw OVFLogicError(N_("Root element in OVF file must be \"Envelope\"."));
 
46
 
 
47
    // OVF has the following rough layout:
 
48
    /*
 
49
        -- <References> ....  files referenced from other parts of the file, such as VMDK images
 
50
        -- Metadata, comprised of several section commands
 
51
        -- virtual machines, either a single <VirtualSystem>, or a <VirtualSystemCollection>
 
52
        -- optionally <Strings> for localization
 
53
    */
 
54
 
 
55
    // get all "File" child elements of "References" section so we can look up files easily;
 
56
    // first find the "References" sections so we can look up files
 
57
    xml::ElementNodesList listFileElements;      // receives all /Envelope/References/File nodes
 
58
    const xml::ElementNode *pReferencesElem;
 
59
    if ((pReferencesElem = pRootElem->findChildElement("References")))
 
60
        pReferencesElem->getChildElements(listFileElements, "File");
 
61
 
 
62
    // now go though the sections
 
63
    LoopThruSections(pReferencesElem, pRootElem);
 
64
}
 
65
 
 
66
OVFReader::~OVFReader()
 
67
{
 
68
}
 
69
 
 
70
/**
 
71
 * Private helper method that goes thru the elements of the given "current" element in the OVF XML
 
72
 * and handles the contained child elements (which can be "Section" or "Content" elements).
 
73
 *
 
74
 * @param pcszPath Path spec of the XML file, for error messages.
 
75
 * @param pReferencesElement "References" element from OVF, for looking up file specifications; can be NULL if no such element is present.
 
76
 * @param pCurElem Element whose children are to be analyzed here.
 
77
 * @return
 
78
 */
 
79
void OVFReader::LoopThruSections(const xml::ElementNode *pReferencesElem,
 
80
                                 const xml::ElementNode *pCurElem)
 
81
{
 
82
    xml::NodesLoop loopChildren(*pCurElem);
 
83
    const xml::ElementNode *pElem;
 
84
    while ((pElem = loopChildren.forAllNodes()))
 
85
    {
 
86
        const char *pcszElemName = pElem->getName();
 
87
        const char *pcszTypeAttr = "";
 
88
        const xml::AttributeNode *pTypeAttr;
 
89
        if ((pTypeAttr = pElem->findAttribute("type")))
 
90
            pcszTypeAttr = pTypeAttr->getValue();
 
91
 
 
92
        if (    (!strcmp(pcszElemName, "DiskSection"))
 
93
             || (    (!strcmp(pcszElemName, "Section"))
 
94
                  && (!strcmp(pcszTypeAttr, "ovf:DiskSection_Type"))
 
95
                )
 
96
           )
 
97
        {
 
98
            HandleDiskSection(pReferencesElem, pElem);
 
99
        }
 
100
       else if (    (!strcmp(pcszElemName, "NetworkSection"))
 
101
                  || (    (!strcmp(pcszElemName, "Section"))
 
102
                       && (!strcmp(pcszTypeAttr, "ovf:NetworkSection_Type"))
 
103
                     )
 
104
                )
 
105
        {
 
106
            HandleNetworkSection(pElem);
 
107
        }
 
108
        else if (    (!strcmp(pcszElemName, "DeploymentOptionSection")))
 
109
        {
 
110
            // TODO
 
111
        }
 
112
        else if (    (!strcmp(pcszElemName, "Info")))
 
113
        {
 
114
            // child of VirtualSystemCollection -- TODO
 
115
        }
 
116
        else if (    (!strcmp(pcszElemName, "ResourceAllocationSection")))
 
117
        {
 
118
            // child of VirtualSystemCollection -- TODO
 
119
        }
 
120
        else if (    (!strcmp(pcszElemName, "StartupSection")))
 
121
        {
 
122
            // child of VirtualSystemCollection -- TODO
 
123
        }
 
124
        else if (    (!strcmp(pcszElemName, "VirtualSystem"))
 
125
                  || (    (!strcmp(pcszElemName, "Content"))
 
126
                       && (!strcmp(pcszTypeAttr, "ovf:VirtualSystem_Type"))
 
127
                     )
 
128
                )
 
129
        {
 
130
            HandleVirtualSystemContent(pElem);
 
131
        }
 
132
        else if (    (!strcmp(pcszElemName, "VirtualSystemCollection"))
 
133
                  || (    (!strcmp(pcszElemName, "Content"))
 
134
                       && (!strcmp(pcszTypeAttr, "ovf:VirtualSystemCollection_Type"))
 
135
                     )
 
136
                )
 
137
        {
 
138
            // TODO ResourceAllocationSection
 
139
 
 
140
            // recurse for this, since it has VirtualSystem elements as children
 
141
            LoopThruSections(pReferencesElem, pElem);
 
142
        }
 
143
    }
 
144
}
 
145
 
 
146
/**
 
147
 * Private helper method that handles disk sections in the OVF XML.
 
148
 * Gets called indirectly from IAppliance::read().
 
149
 *
 
150
 * @param pcszPath Path spec of the XML file, for error messages.
 
151
 * @param pReferencesElement "References" element from OVF, for looking up file specifications; can be NULL if no such element is present.
 
152
 * @param pSectionElem Section element for which this helper is getting called.
 
153
 * @return
 
154
 */
 
155
void OVFReader::HandleDiskSection(const xml::ElementNode *pReferencesElem,
 
156
                                  const xml::ElementNode *pSectionElem)
 
157
{
 
158
    // contains "Disk" child elements
 
159
    xml::NodesLoop loopDisks(*pSectionElem, "Disk");
 
160
    const xml::ElementNode *pelmDisk;
 
161
    while ((pelmDisk = loopDisks.forAllNodes()))
 
162
    {
 
163
        DiskImage d;
 
164
        const char *pcszBad = NULL;
 
165
        const char *pcszDiskId;
 
166
        const char *pcszFormat;
 
167
        if (!(pelmDisk->getAttributeValue("diskId", pcszDiskId)))
 
168
            pcszBad = "diskId";
 
169
        else if (!(pelmDisk->getAttributeValue("format", pcszFormat)))
 
170
            pcszBad = "format";
 
171
        else if (!(pelmDisk->getAttributeValue("capacity", d.iCapacity)))
 
172
            pcszBad = "capacity";
 
173
        else
 
174
        {
 
175
            d.strDiskId = pcszDiskId;
 
176
            d.strFormat = pcszFormat;
 
177
 
 
178
            if (!(pelmDisk->getAttributeValue("populatedSize", d.iPopulatedSize)))
 
179
                // optional
 
180
                d.iPopulatedSize = -1;
 
181
 
 
182
            const char *pcszFileRef;
 
183
            if (pelmDisk->getAttributeValue("fileRef", pcszFileRef)) // optional
 
184
            {
 
185
                // look up corresponding /References/File nodes (list built above)
 
186
                const xml::ElementNode *pFileElem;
 
187
                if (    pReferencesElem
 
188
                     && ((pFileElem = pReferencesElem->findChildElementFromId(pcszFileRef)))
 
189
                   )
 
190
                {
 
191
                    // copy remaining values from file node then
 
192
                    const char *pcszBadInFile = NULL;
 
193
                    const char *pcszHref;
 
194
                    if (!(pFileElem->getAttributeValue("href", pcszHref)))
 
195
                        pcszBadInFile = "href";
 
196
                    else if (!(pFileElem->getAttributeValue("size", d.iSize)))
 
197
                        d.iSize = -1;       // optional
 
198
 
 
199
                    d.strHref = pcszHref;
 
200
 
 
201
                    // if (!(pFileElem->getAttributeValue("size", d.iChunkSize))) TODO
 
202
                    d.iChunkSize = -1;       // optional
 
203
                    const char *pcszCompression;
 
204
                    if (pFileElem->getAttributeValue("compression", pcszCompression))
 
205
                        d.strCompression = pcszCompression;
 
206
 
 
207
                    if (pcszBadInFile)
 
208
                        throw OVFLogicError(N_("Error reading \"%s\": missing or invalid attribute '%s' in 'File' element, line %d"),
 
209
                                            m_strPath.c_str(),
 
210
                                            pcszBadInFile,
 
211
                                            pFileElem->getLineNumber());
 
212
                }
 
213
                else
 
214
                    throw OVFLogicError(N_("Error reading \"%s\": cannot find References/File element for ID '%s' referenced by 'Disk' element, line %d"),
 
215
                                        m_strPath.c_str(),
 
216
                                        pcszFileRef,
 
217
                                        pelmDisk->getLineNumber());
 
218
            }
 
219
        }
 
220
 
 
221
        if (pcszBad)
 
222
            throw OVFLogicError(N_("Error reading \"%s\": missing or invalid attribute '%s' in 'DiskSection' element, line %d"),
 
223
                                m_strPath.c_str(),
 
224
                                pcszBad,
 
225
                                pelmDisk->getLineNumber());
 
226
 
 
227
        m_mapDisks[d.strDiskId] = d;
 
228
    }
 
229
}
 
230
 
 
231
/**
 
232
 * Private helper method that handles network sections in the OVF XML.
 
233
 * Gets called indirectly from IAppliance::read().
 
234
 *
 
235
 * @param pcszPath Path spec of the XML file, for error messages.
 
236
 * @param pSectionElem Section element for which this helper is getting called.
 
237
 * @return
 
238
 */
 
239
void OVFReader::HandleNetworkSection(const xml::ElementNode * /* pSectionElem */)
 
240
{
 
241
    // we ignore network sections for now
 
242
 
 
243
//     xml::NodesLoop loopNetworks(*pSectionElem, "Network");
 
244
//     const xml::Node *pelmNetwork;
 
245
//     while ((pelmNetwork = loopNetworks.forAllNodes()))
 
246
//     {
 
247
//         Network n;
 
248
//         if (!(pelmNetwork->getAttributeValue("name", n.strNetworkName)))
 
249
//             return setError(VBOX_E_FILE_ERROR,
 
250
//                             tr("Error reading \"%s\": missing 'name' attribute in 'Network', line %d"),
 
251
//                             pcszPath,
 
252
//                             pelmNetwork->getLineNumber());
 
253
//
 
254
//         m->mapNetworks[n.strNetworkName] = n;
 
255
//     }
 
256
}
 
257
 
 
258
/**
 
259
 * Private helper method that handles a "VirtualSystem" element in the OVF XML.
 
260
 * Gets called indirectly from IAppliance::read().
 
261
 *
 
262
 * @param pcszPath
 
263
 * @param pContentElem
 
264
 * @return
 
265
 */
 
266
void OVFReader::HandleVirtualSystemContent(const xml::ElementNode *pelmVirtualSystem)
 
267
{
 
268
    VirtualSystem vsys;
 
269
 
 
270
    const xml::AttributeNode *pIdAttr = pelmVirtualSystem->findAttribute("id");
 
271
    if (pIdAttr)
 
272
        vsys.strName = pIdAttr->getValue();
 
273
 
 
274
    xml::NodesLoop loop(*pelmVirtualSystem);      // all child elements
 
275
    const xml::ElementNode *pelmThis;
 
276
    while ((pelmThis = loop.forAllNodes()))
 
277
    {
 
278
        const char *pcszElemName = pelmThis->getName();
 
279
        const xml::AttributeNode *pTypeAttr = pelmThis->findAttribute("type");
 
280
        const char *pcszTypeAttr = (pTypeAttr) ? pTypeAttr->getValue() : "";
 
281
 
 
282
        if (    (!strcmp(pcszElemName, "EulaSection"))
 
283
             || (!strcmp(pcszTypeAttr, "ovf:EulaSection_Type"))
 
284
           )
 
285
        {
 
286
         /* <EulaSection>
 
287
                <Info ovf:msgid="6">License agreement for the Virtual System.</Info>
 
288
                <License ovf:msgid="1">License terms can go in here.</License>
 
289
            </EulaSection> */
 
290
 
 
291
            const xml::ElementNode *pelmLicense;
 
292
            if ((pelmLicense = pelmThis->findChildElement("License")))
 
293
                vsys.strLicenseText = pelmLicense->getValue();
 
294
        }
 
295
        if (    (!strcmp(pcszElemName, "ProductSection"))
 
296
             || (!strcmp(pcszTypeAttr, "ovf:ProductSection_Type"))
 
297
           )
 
298
        {
 
299
            /* <Section ovf:required="false" xsi:type="ovf:ProductSection_Type">
 
300
                <Info>Meta-information about the installed software</Info>
 
301
                <Product>VAtest</Product>
 
302
                <Vendor>SUN Microsystems</Vendor>
 
303
                <Version>10.0</Version>
 
304
                <ProductUrl>http://blogs.sun.com/VirtualGuru</ProductUrl>
 
305
                <VendorUrl>http://www.sun.com</VendorUrl>
 
306
               </Section> */
 
307
            const xml::ElementNode *pelmProduct;
 
308
            if ((pelmProduct = pelmThis->findChildElement("Product")))
 
309
                vsys.strProduct = pelmProduct->getValue();
 
310
            const xml::ElementNode *pelmVendor;
 
311
            if ((pelmVendor = pelmThis->findChildElement("Vendor")))
 
312
                vsys.strVendor = pelmVendor->getValue();
 
313
            const xml::ElementNode *pelmVersion;
 
314
            if ((pelmVersion = pelmThis->findChildElement("Version")))
 
315
                vsys.strVersion = pelmVersion->getValue();
 
316
            const xml::ElementNode *pelmProductUrl;
 
317
            if ((pelmProductUrl = pelmThis->findChildElement("ProductUrl")))
 
318
                vsys.strProductUrl = pelmProductUrl->getValue();
 
319
            const xml::ElementNode *pelmVendorUrl;
 
320
            if ((pelmVendorUrl = pelmThis->findChildElement("VendorUrl")))
 
321
                vsys.strVendorUrl = pelmVendorUrl->getValue();
 
322
        }
 
323
        else if (    (!strcmp(pcszElemName, "VirtualHardwareSection"))
 
324
                  || (!strcmp(pcszTypeAttr, "ovf:VirtualHardwareSection_Type"))
 
325
                )
 
326
        {
 
327
            const xml::ElementNode *pelmSystem, *pelmVirtualSystemType;
 
328
            if ((pelmSystem = pelmThis->findChildElement("System")))
 
329
            {
 
330
             /* <System>
 
331
                    <vssd:Description>Description of the virtual hardware section.</vssd:Description>
 
332
                    <vssd:ElementName>vmware</vssd:ElementName>
 
333
                    <vssd:InstanceID>1</vssd:InstanceID>
 
334
                    <vssd:VirtualSystemIdentifier>MyLampService</vssd:VirtualSystemIdentifier>
 
335
                    <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType>
 
336
                </System>*/
 
337
                if ((pelmVirtualSystemType = pelmSystem->findChildElement("VirtualSystemType")))
 
338
                    vsys.strVirtualSystemType = pelmVirtualSystemType->getValue();
 
339
            }
 
340
 
 
341
            xml::NodesLoop loopVirtualHardwareItems(*pelmThis, "Item");      // all "Item" child elements
 
342
            const xml::ElementNode *pelmItem;
 
343
            while ((pelmItem = loopVirtualHardwareItems.forAllNodes()))
 
344
            {
 
345
                VirtualHardwareItem i;
 
346
 
 
347
                i.ulLineNumber = pelmItem->getLineNumber();
 
348
 
 
349
                xml::NodesLoop loopItemChildren(*pelmItem);      // all child elements
 
350
                const xml::ElementNode *pelmItemChild;
 
351
                while ((pelmItemChild = loopItemChildren.forAllNodes()))
 
352
                {
 
353
                    const char *pcszItemChildName = pelmItemChild->getName();
 
354
                    if (!strcmp(pcszItemChildName, "Description"))
 
355
                        i.strDescription = pelmItemChild->getValue();
 
356
                    else if (!strcmp(pcszItemChildName, "Caption"))
 
357
                        i.strCaption = pelmItemChild->getValue();
 
358
                    else if (!strcmp(pcszItemChildName, "ElementName"))
 
359
                        i.strElementName = pelmItemChild->getValue();
 
360
                    else if (    (!strcmp(pcszItemChildName, "InstanceID"))
 
361
                              || (!strcmp(pcszItemChildName, "InstanceId"))
 
362
                            )
 
363
                        pelmItemChild->copyValue(i.ulInstanceID);
 
364
                    else if (!strcmp(pcszItemChildName, "HostResource"))
 
365
                        i.strHostResource = pelmItemChild->getValue();
 
366
                    else if (!strcmp(pcszItemChildName, "ResourceType"))
 
367
                    {
 
368
                        uint32_t ulType;
 
369
                        pelmItemChild->copyValue(ulType);
 
370
                        i.resourceType = (OVFResourceType_T)ulType;
 
371
                    }
 
372
                    else if (!strcmp(pcszItemChildName, "OtherResourceType"))
 
373
                        i.strOtherResourceType = pelmItemChild->getValue();
 
374
                    else if (!strcmp(pcszItemChildName, "ResourceSubType"))
 
375
                        i.strResourceSubType = pelmItemChild->getValue();
 
376
                    else if (!strcmp(pcszItemChildName, "AutomaticAllocation"))
 
377
                        i.fAutomaticAllocation = (!strcmp(pelmItemChild->getValue(), "true")) ? true : false;
 
378
                    else if (!strcmp(pcszItemChildName, "AutomaticDeallocation"))
 
379
                        i.fAutomaticDeallocation = (!strcmp(pelmItemChild->getValue(), "true")) ? true : false;
 
380
                    else if (!strcmp(pcszItemChildName, "Parent"))
 
381
                        pelmItemChild->copyValue(i.ulParent);
 
382
                    else if (!strcmp(pcszItemChildName, "Connection"))
 
383
                        i.strConnection = pelmItemChild->getValue();
 
384
                    else if (!strcmp(pcszItemChildName, "Address"))
 
385
                        i.strAddress = pelmItemChild->getValue();
 
386
                    else if (!strcmp(pcszItemChildName, "AddressOnParent"))
 
387
                        i.strAddressOnParent = pelmItemChild->getValue();
 
388
                    else if (!strcmp(pcszItemChildName, "AllocationUnits"))
 
389
                        i.strAllocationUnits = pelmItemChild->getValue();
 
390
                    else if (!strcmp(pcszItemChildName, "VirtualQuantity"))
 
391
                        pelmItemChild->copyValue(i.ullVirtualQuantity);
 
392
                    else if (!strcmp(pcszItemChildName, "Reservation"))
 
393
                        pelmItemChild->copyValue(i.ullReservation);
 
394
                    else if (!strcmp(pcszItemChildName, "Limit"))
 
395
                        pelmItemChild->copyValue(i.ullLimit);
 
396
                    else if (!strcmp(pcszItemChildName, "Weight"))
 
397
                        pelmItemChild->copyValue(i.ullWeight);
 
398
                    else if (!strcmp(pcszItemChildName, "ConsumerVisibility"))
 
399
                        i.strConsumerVisibility = pelmItemChild->getValue();
 
400
                    else if (!strcmp(pcszItemChildName, "MappingBehavior"))
 
401
                        i.strMappingBehavior = pelmItemChild->getValue();
 
402
                    else if (!strcmp(pcszItemChildName, "PoolID"))
 
403
                        i.strPoolID = pelmItemChild->getValue();
 
404
                    else if (!strcmp(pcszItemChildName, "BusNumber"))
 
405
                        pelmItemChild->copyValue(i.ulBusNumber);
 
406
                    else
 
407
                        throw OVFLogicError(N_("Error reading \"%s\": unknown element \"%s\" under Item element, line %d"),
 
408
                                            m_strPath.c_str(),
 
409
                                            pcszItemChildName,
 
410
                                            i.ulLineNumber);
 
411
                }
 
412
 
 
413
                // store!
 
414
                vsys.mapHardwareItems[i.ulInstanceID] = i;
 
415
            }
 
416
 
 
417
            // now go thru all hardware items and handle them according to their type;
 
418
            // in this first loop we handle all items _except_ hard disk images,
 
419
            // which we'll handle in a second loop below
 
420
            HardwareItemsMap::const_iterator itH;
 
421
            for (itH = vsys.mapHardwareItems.begin();
 
422
                 itH != vsys.mapHardwareItems.end();
 
423
                 ++itH)
 
424
            {
 
425
                const VirtualHardwareItem &i = itH->second;
 
426
 
 
427
                // do some analysis
 
428
                switch (i.resourceType)
 
429
                {
 
430
                    case OVFResourceType_Processor:     // 3
 
431
                        /*  <rasd:Caption>1 virtual CPU</rasd:Caption>
 
432
                            <rasd:Description>Number of virtual CPUs</rasd:Description>
 
433
                            <rasd:ElementName>virtual CPU</rasd:ElementName>
 
434
                            <rasd:InstanceID>1</rasd:InstanceID>
 
435
                            <rasd:ResourceType>3</rasd:ResourceType>
 
436
                            <rasd:VirtualQuantity>1</rasd:VirtualQuantity>*/
 
437
                        if (i.ullVirtualQuantity < UINT16_MAX)
 
438
                            vsys.cCPUs = (uint16_t)i.ullVirtualQuantity;
 
439
                        else
 
440
                            throw OVFLogicError(N_("Error reading \"%s\": CPU count %RI64 is larger than %d, line %d"),
 
441
                                                m_strPath.c_str(),
 
442
                                                i.ullVirtualQuantity,
 
443
                                                UINT16_MAX,
 
444
                                                i.ulLineNumber);
 
445
                    break;
 
446
 
 
447
                    case OVFResourceType_Memory:        // 4
 
448
                        if (    (i.strAllocationUnits == "MegaBytes")           // found in OVF created by OVF toolkit
 
449
                             || (i.strAllocationUnits == "MB")                  // found in MS docs
 
450
                             || (i.strAllocationUnits == "byte * 2^20")         // suggested by OVF spec DSP0243 page 21
 
451
                           )
 
452
                            vsys.ullMemorySize = i.ullVirtualQuantity * 1024 * 1024;
 
453
                        else
 
454
                            throw OVFLogicError(N_("Error reading \"%s\": Invalid allocation unit \"%s\" specified with memory size item, line %d"),
 
455
                                                m_strPath.c_str(),
 
456
                                                i.strAllocationUnits.c_str(),
 
457
                                                i.ulLineNumber);
 
458
                    break;
 
459
 
 
460
                    case OVFResourceType_IDEController:          // 5
 
461
                    {
 
462
                        /*  <Item>
 
463
                                <rasd:Caption>ideController0</rasd:Caption>
 
464
                                <rasd:Description>IDE Controller</rasd:Description>
 
465
                                <rasd:InstanceId>5</rasd:InstanceId>
 
466
                                <rasd:ResourceType>5</rasd:ResourceType>
 
467
                                <rasd:Address>0</rasd:Address>
 
468
                                <rasd:BusNumber>0</rasd:BusNumber>
 
469
                            </Item> */
 
470
                        HardDiskController hdc;
 
471
                        hdc.system = HardDiskController::IDE;
 
472
                        hdc.idController = i.ulInstanceID;
 
473
                        hdc.strControllerType = i.strResourceSubType;
 
474
                        hdc.strAddress = i.strAddress;
 
475
                        hdc.ulBusNumber = i.ulBusNumber;
 
476
 
 
477
                        vsys.mapControllers[i.ulInstanceID] = hdc;
 
478
                    }
 
479
                    break;
 
480
 
 
481
                    case OVFResourceType_ParallelSCSIHBA:        // 6       SCSI controller
 
482
                    {
 
483
                        /*  <Item>
 
484
                                <rasd:Caption>SCSI Controller 0 - LSI Logic</rasd:Caption>
 
485
                                <rasd:Description>SCI Controller</rasd:Description>
 
486
                                <rasd:ElementName>SCSI controller</rasd:ElementName>
 
487
                                <rasd:InstanceID>4</rasd:InstanceID>
 
488
                                <rasd:ResourceSubType>LsiLogic</rasd:ResourceSubType>
 
489
                                <rasd:ResourceType>6</rasd:ResourceType>
 
490
                            </Item> */
 
491
                        HardDiskController hdc;
 
492
                        hdc.system = HardDiskController::SCSI;
 
493
                        hdc.idController = i.ulInstanceID;
 
494
                        hdc.strControllerType = i.strResourceSubType;
 
495
 
 
496
                        vsys.mapControllers[i.ulInstanceID] = hdc;
 
497
                    }
 
498
                    break;
 
499
 
 
500
                    case OVFResourceType_EthernetAdapter: // 10
 
501
                    {
 
502
                        /*  <Item>
 
503
                            <rasd:Caption>Ethernet adapter on 'Bridged'</rasd:Caption>
 
504
                            <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
 
505
                            <rasd:Connection>Bridged</rasd:Connection>
 
506
                            <rasd:InstanceID>6</rasd:InstanceID>
 
507
                            <rasd:ResourceType>10</rasd:ResourceType>
 
508
                            <rasd:ResourceSubType>E1000</rasd:ResourceSubType>
 
509
                            </Item>
 
510
 
 
511
                            OVF spec DSP 0243 page 21:
 
512
                           "For an Ethernet adapter, this specifies the abstract network connection name
 
513
                            for the virtual machine. All Ethernet adapters that specify the same abstract
 
514
                            network connection name within an OVF package shall be deployed on the same
 
515
                            network. The abstract network connection name shall be listed in the NetworkSection
 
516
                            at the outermost envelope level." */
 
517
 
 
518
                        // only store the name
 
519
                        EthernetAdapter ea;
 
520
                        ea.strAdapterType = i.strResourceSubType;
 
521
                        ea.strNetworkName = i.strConnection;
 
522
                        vsys.llEthernetAdapters.push_back(ea);
 
523
                    }
 
524
                    break;
 
525
 
 
526
                    case OVFResourceType_FloppyDrive: // 14
 
527
                        vsys.fHasFloppyDrive = true;           // we have no additional information
 
528
                    break;
 
529
 
 
530
                    case OVFResourceType_CDDrive:       // 15
 
531
                        /*  <Item ovf:required="false">
 
532
                                <rasd:Caption>cdrom1</rasd:Caption>
 
533
                                <rasd:InstanceId>7</rasd:InstanceId>
 
534
                                <rasd:ResourceType>15</rasd:ResourceType>
 
535
                                <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
 
536
                                <rasd:Parent>5</rasd:Parent>
 
537
                                <rasd:AddressOnParent>0</rasd:AddressOnParent>
 
538
                            </Item> */
 
539
                            // I tried to see what happens if I set an ISO for the CD-ROM in VMware Workstation,
 
540
                            // but then the ovftool dies with "Device backing not supported". So I guess if
 
541
                            // VMware can't export ISOs, then we don't need to be able to import them right now.
 
542
                        vsys.fHasCdromDrive = true;           // we have no additional information
 
543
                    break;
 
544
 
 
545
                    case OVFResourceType_HardDisk: // 17
 
546
                        // handled separately in second loop below
 
547
                    break;
 
548
 
 
549
                    case OVFResourceType_OtherStorageDevice:        // 20       SATA controller
 
550
                    {
 
551
                        /* <Item>
 
552
                            <rasd:Description>SATA Controller</rasd:Description>
 
553
                            <rasd:Caption>sataController0</rasd:Caption>
 
554
                            <rasd:InstanceID>4</rasd:InstanceID>
 
555
                            <rasd:ResourceType>20</rasd:ResourceType>
 
556
                            <rasd:ResourceSubType>AHCI</rasd:ResourceSubType>
 
557
                            <rasd:Address>0</rasd:Address>
 
558
                            <rasd:BusNumber>0</rasd:BusNumber>
 
559
                        </Item> */
 
560
                        if (    i.strCaption.startsWith("sataController", MiniString::CaseInsensitive)
 
561
                             && !i.strResourceSubType.compare("AHCI", MiniString::CaseInsensitive)
 
562
                           )
 
563
                        {
 
564
                            HardDiskController hdc;
 
565
                            hdc.system = HardDiskController::SATA;
 
566
                            hdc.idController = i.ulInstanceID;
 
567
                            hdc.strControllerType = i.strResourceSubType;
 
568
 
 
569
                            vsys.mapControllers[i.ulInstanceID] = hdc;
 
570
                        }
 
571
                        else
 
572
                            throw OVFLogicError(N_("Error reading \"%s\": Host resource of type \"Other Storage Device (%d)\" is supported with SATA AHCI controllers only, line %d"),
 
573
                                                m_strPath.c_str(),
 
574
                                                OVFResourceType_OtherStorageDevice,
 
575
                                                i.ulLineNumber);
 
576
                    }
 
577
                    break;
 
578
 
 
579
                    case OVFResourceType_USBController: // 23
 
580
                        /*  <Item ovf:required="false">
 
581
                                <rasd:Caption>usb</rasd:Caption>
 
582
                                <rasd:Description>USB Controller</rasd:Description>
 
583
                                <rasd:InstanceId>3</rasd:InstanceId>
 
584
                                <rasd:ResourceType>23</rasd:ResourceType>
 
585
                                <rasd:Address>0</rasd:Address>
 
586
                                <rasd:BusNumber>0</rasd:BusNumber>
 
587
                            </Item> */
 
588
                        vsys.fHasUsbController = true;           // we have no additional information
 
589
                    break;
 
590
 
 
591
                    case OVFResourceType_SoundCard: // 35
 
592
                        /*  <Item ovf:required="false">
 
593
                                <rasd:Caption>sound</rasd:Caption>
 
594
                                <rasd:Description>Sound Card</rasd:Description>
 
595
                                <rasd:InstanceId>10</rasd:InstanceId>
 
596
                                <rasd:ResourceType>35</rasd:ResourceType>
 
597
                                <rasd:ResourceSubType>ensoniq1371</rasd:ResourceSubType>
 
598
                                <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
 
599
                                <rasd:AddressOnParent>3</rasd:AddressOnParent>
 
600
                            </Item> */
 
601
                        vsys.strSoundCardType = i.strResourceSubType;
 
602
                    break;
 
603
 
 
604
                    default:
 
605
                        throw OVFLogicError(N_("Error reading \"%s\": Unknown resource type %d in hardware item, line %d"),
 
606
                                            m_strPath.c_str(),
 
607
                                            i.resourceType,
 
608
                                            i.ulLineNumber);
 
609
                } // end switch
 
610
            }
 
611
 
 
612
            // now run through the items for a second time, but handle only
 
613
            // hard disk images; otherwise the code would fail if a hard
 
614
            // disk image appears in the OVF before its hard disk controller
 
615
            for (itH = vsys.mapHardwareItems.begin();
 
616
                 itH != vsys.mapHardwareItems.end();
 
617
                 ++itH)
 
618
            {
 
619
                const VirtualHardwareItem &i = itH->second;
 
620
 
 
621
                // do some analysis
 
622
                switch (i.resourceType)
 
623
                {
 
624
                    case OVFResourceType_HardDisk: // 17
 
625
                    {
 
626
                        /*  <Item>
 
627
                                <rasd:Caption>Harddisk 1</rasd:Caption>
 
628
                                <rasd:Description>HD</rasd:Description>
 
629
                                <rasd:ElementName>Hard Disk</rasd:ElementName>
 
630
                                <rasd:HostResource>ovf://disk/lamp</rasd:HostResource>
 
631
                                <rasd:InstanceID>5</rasd:InstanceID>
 
632
                                <rasd:Parent>4</rasd:Parent>
 
633
                                <rasd:ResourceType>17</rasd:ResourceType>
 
634
                            </Item> */
 
635
 
 
636
                        // look up the hard disk controller element whose InstanceID equals our Parent;
 
637
                        // this is how the connection is specified in OVF
 
638
                        ControllersMap::const_iterator it = vsys.mapControllers.find(i.ulParent);
 
639
                        if (it == vsys.mapControllers.end())
 
640
                            throw OVFLogicError(N_("Error reading \"%s\": Hard disk item with instance ID %d specifies invalid parent %d, line %d"),
 
641
                                                m_strPath.c_str(),
 
642
                                                i.ulInstanceID,
 
643
                                                i.ulParent,
 
644
                                                i.ulLineNumber);
 
645
                        //const HardDiskController &hdc = it->second;
 
646
 
 
647
                        VirtualDisk vd;
 
648
                        vd.idController = i.ulParent;
 
649
                        i.strAddressOnParent.toInt(vd.ulAddressOnParent);
 
650
                        // ovf://disk/lamp
 
651
                        // 123456789012345
 
652
                        if (i.strHostResource.substr(0, 11) == "ovf://disk/")
 
653
                            vd.strDiskId = i.strHostResource.substr(11);
 
654
                        else if (i.strHostResource.substr(0, 10) == "ovf:/disk/")
 
655
                            vd.strDiskId = i.strHostResource.substr(10);
 
656
                        else if (i.strHostResource.substr(0, 6) == "/disk/")
 
657
                            vd.strDiskId = i.strHostResource.substr(6);
 
658
 
 
659
                        if (    !(vd.strDiskId.length())
 
660
                             || (m_mapDisks.find(vd.strDiskId) == m_mapDisks.end())
 
661
                           )
 
662
                            throw OVFLogicError(N_("Error reading \"%s\": Hard disk item with instance ID %d specifies invalid host resource \"%s\", line %d"),
 
663
                                                m_strPath.c_str(),
 
664
                                                i.ulInstanceID,
 
665
                                                i.strHostResource.c_str(),
 
666
                                                i.ulLineNumber);
 
667
 
 
668
                        vsys.mapVirtualDisks[vd.strDiskId] = vd;
 
669
                    }
 
670
                    break;
 
671
                    default: break;
 
672
                }
 
673
            }
 
674
        }
 
675
        else if (    (!strcmp(pcszElemName, "OperatingSystemSection"))
 
676
                  || (!strcmp(pcszTypeAttr, "ovf:OperatingSystemSection_Type"))
 
677
                )
 
678
        {
 
679
            uint64_t cimos64;
 
680
            if (!(pelmThis->getAttributeValue("id", cimos64)))
 
681
                throw OVFLogicError(N_("Error reading \"%s\": missing or invalid 'ovf:id' attribute in operating system section element, line %d"),
 
682
                                    m_strPath.c_str(),
 
683
                                    pelmThis->getLineNumber());
 
684
 
 
685
            vsys.cimos = (CIMOSType_T)cimos64;
 
686
            const xml::ElementNode *pelmCIMOSDescription;
 
687
            if ((pelmCIMOSDescription = pelmThis->findChildElement("Description")))
 
688
                vsys.strCimosDesc = pelmCIMOSDescription->getValue();
 
689
        }
 
690
        else if (    (!strcmp(pcszElemName, "AnnotationSection"))
 
691
                  || (!strcmp(pcszTypeAttr, "ovf:AnnotationSection_Type"))
 
692
                )
 
693
        {
 
694
            const xml::ElementNode *pelmAnnotation;
 
695
            if ((pelmAnnotation = pelmThis->findChildElement("Annotation")))
 
696
                vsys.strDescription = pelmAnnotation->getValue();
 
697
        }
 
698
    }
 
699
 
 
700
    // now create the virtual system
 
701
    m_llVirtualSystems.push_back(vsys);
 
702
}
 
703
 
 
704
////////////////////////////////////////////////////////////////////////////////
 
705
//
 
706
// Errors
 
707
//
 
708
////////////////////////////////////////////////////////////////////////////////
 
709
 
 
710
OVFLogicError::OVFLogicError(const char *aFormat, ...)
 
711
{
 
712
    char *pszNewMsg;
 
713
    va_list args;
 
714
    va_start(args, aFormat);
 
715
    RTStrAPrintfV(&pszNewMsg, aFormat, args);
 
716
    setWhat(pszNewMsg);
 
717
    RTStrFree(pszNewMsg);
 
718
    va_end(args);
 
719
}