- Timestamp:
- Apr 8, 2010 3:35:38 PM (15 years ago)
- Location:
- trunk
- Files:
-
- 6 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/include/VBox/settings.h
r27918 r28098 856 856 857 857 void write(const com::Utf8Str &strFilename); 858 void buildMachineXML(xml::ElementNode &elmMachine );858 void buildMachineXML(xml::ElementNode &elmMachine, bool fIncludeSnapshots); 859 859 860 860 private: -
trunk/src/VBox/Main/ApplianceImplExport.cpp
r27908 r28098 1228 1228 AutoWriteLock machineLock(vsdescThis->m->pMachine COMMA_LOCKVAL_SRC_POS); 1229 1229 vsdescThis->m->pMachine->copyMachineDataToSettings(*pConfig); 1230 pConfig->buildMachineXML(*pelmVBoxMachine); 1230 pConfig->buildMachineXML(*pelmVBoxMachine, 1231 false /* fIncludeSnapshots */); 1231 1232 delete pConfig; 1232 1233 } -
trunk/src/VBox/Main/ApplianceImplImport.cpp
r27918 r28098 952 952 953 953 /** 954 * Used by Appliance::importMachineGeneric() to store 955 * input parameters and rollback information. 956 */ 957 struct Appliance::ImportStack 958 { 959 // input pointers 960 const LocationInfo &locInfo; // ptr to location info from Appliance::importFS() 961 const ovf::DiskImagesMap &mapDisks; // ptr to disks map in OVF 962 ComObjPtr<Progress> &pProgress; // progress object passed into Appliance::importFS() 963 964 // session (not initially created) 965 ComPtr<ISession> pSession; // session opened in Appliance::importFS() for machine manipulation 966 bool fSessionOpen; // true if the pSession is currently open and needs closing 967 968 // a list of images that we created/imported; this is initially empty 969 // and will be cleaned up on errors 970 list<MyHardDiskAttachment> llHardDiskAttachments; // disks that were attached 971 list< ComPtr<IMedium> > llHardDisksCreated; // media that were created 972 list<Bstr> llMachinesRegistered; // machines that were registered; list of string UUIDs 973 974 ImportStack(const LocationInfo &aLocInfo, 975 const ovf::DiskImagesMap &aMapDisks, 976 ComObjPtr<Progress> &aProgress) 977 : locInfo(aLocInfo), 978 mapDisks(aMapDisks), 979 pProgress(aProgress), 980 fSessionOpen(false) 981 { 982 } 983 }; 984 985 /** 954 986 * Actual worker code for importing OVF data into VirtualBox. This is called from Appliance::taskThreadImportOrExport() 955 987 * and therefore runs on the OVF import worker thread. This runs in two contexts: … … 963 995 * @return 964 996 */ 965 HRESULT Appliance::importFS(const LocationInfo &locInfo, ComObjPtr<Progress> &pProgress) 997 HRESULT Appliance::importFS(const LocationInfo &locInfo, 998 ComObjPtr<Progress> &pProgress) 966 999 { 967 1000 LogFlowFuncEnter(); … … 975 1008 if (!isApplianceIdle()) 976 1009 return E_ACCESSDENIED; 1010 1011 Assert(!pProgress.isNull()); 977 1012 978 1013 // Change the appliance state so we can safely leave the lock while doing time-consuming … … 984 1019 HRESULT rc = S_OK; 985 1020 986 // rollback for errors:987 // a list of images that we created/imported988 list<MyHardDiskAttachment> llHardDiskAttachments;989 list< ComPtr<IMedium> > llHardDisksCreated;990 list<Bstr> llMachinesRegistered; // list of string UUIDs991 992 ComPtr<ISession> session;993 bool fSessionOpen = false;994 rc = session.createInprocObject(CLSID_Session);995 if (FAILED(rc)) return rc;996 997 1021 const ovf::OVFReader &reader = *m->pReader; 998 1022 // this is safe to access because this thread only gets started 999 1023 // if pReader != NULL 1000 1024 1001 /* If an manifest file exists, verify the content. Therefore we need all 1025 // rollback for errors: 1026 ImportStack stack(locInfo, reader.m_mapDisks, pProgress); 1027 /* If a manifest file exists, verify the content. Therefore we need all 1002 1028 * files which are referenced by the OVF & the OVF itself */ 1003 1029 Utf8Str strMfFile = manifestFileName(locInfo.strPath); … … 1065 1091 } 1066 1092 1093 rc = stack.pSession.createInprocObject(CLSID_Session); 1094 if (FAILED(rc)) return rc; 1095 1067 1096 list<ovf::VirtualSystem>::const_iterator it; 1068 1097 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it1; … … 1070 1099 size_t i = 0; 1071 1100 for (it = reader.m_llVirtualSystems.begin(), 1072 it1 = m->virtualSystemDescriptions.begin();1101 it1 = m->virtualSystemDescriptions.begin(); 1073 1102 it != reader.m_llVirtualSystems.end(); 1074 1103 ++it, ++it1, ++i) … … 1082 1111 try 1083 1112 { 1084 // there are two ways in which we can create 1085 1086 /* Guest OS type */ 1087 std::list<VirtualSystemDescriptionEntry*> vsdeOS; 1088 vsdeOS = vsdescThis->findByType(VirtualSystemDescriptionType_OS); 1089 if (vsdeOS.size() < 1) 1090 throw setError(VBOX_E_FILE_ERROR, 1091 tr("Missing guest OS type")); 1092 const Utf8Str &strOsTypeVBox = vsdeOS.front()->strVbox; 1093 1094 /* Now that we know the base system get our internal defaults based on that. */ 1095 ComPtr<IGuestOSType> osType; 1096 rc = mVirtualBox->GetGuestOSType(Bstr(strOsTypeVBox), osType.asOutParam()); 1097 if (FAILED(rc)) throw rc; 1098 1099 /* Create the machine */ 1100 /* First get the name */ 1101 std::list<VirtualSystemDescriptionEntry*> vsdeName = vsdescThis->findByType(VirtualSystemDescriptionType_Name); 1102 if (vsdeName.size() < 1) 1103 throw setError(VBOX_E_FILE_ERROR, 1104 tr("Missing VM name")); 1105 const Utf8Str &strNameVBox = vsdeName.front()->strVbox; 1106 rc = mVirtualBox->CreateMachine(Bstr(strNameVBox), 1107 Bstr(strOsTypeVBox), 1108 NULL, 1109 NULL, 1110 FALSE, 1111 pNewMachine.asOutParam()); 1112 if (FAILED(rc)) throw rc; 1113 1114 // and the description 1115 std::list<VirtualSystemDescriptionEntry*> vsdeDescription = vsdescThis->findByType(VirtualSystemDescriptionType_Description); 1116 if (vsdeDescription.size()) 1117 { 1118 const Utf8Str &strDescription = vsdeDescription.front()->strVbox; 1119 rc = pNewMachine->COMSETTER(Description)(Bstr(strDescription)); 1120 if (FAILED(rc)) throw rc; 1121 } 1122 1123 /* CPU count */ 1124 std::list<VirtualSystemDescriptionEntry*> vsdeCPU = vsdescThis->findByType(VirtualSystemDescriptionType_CPU); 1125 ComAssertMsgThrow(vsdeCPU.size() == 1, ("CPU count missing"), E_FAIL); 1126 const Utf8Str &cpuVBox = vsdeCPU.front()->strVbox; 1127 ULONG tmpCount = (ULONG)RTStrToUInt64(cpuVBox.c_str()); 1128 rc = pNewMachine->COMSETTER(CPUCount)(tmpCount); 1129 if (FAILED(rc)) throw rc; 1130 bool fEnableIOApic = false; 1131 /* We need HWVirt & IO-APIC if more than one CPU is requested */ 1132 if (tmpCount > 1) 1133 { 1134 rc = pNewMachine->SetHWVirtExProperty(HWVirtExPropertyType_Enabled, TRUE); 1135 if (FAILED(rc)) throw rc; 1136 1137 fEnableIOApic = true; 1138 } 1139 1140 /* RAM */ 1141 std::list<VirtualSystemDescriptionEntry*> vsdeRAM = vsdescThis->findByType(VirtualSystemDescriptionType_Memory); 1142 ComAssertMsgThrow(vsdeRAM.size() == 1, ("RAM size missing"), E_FAIL); 1143 const Utf8Str &memoryVBox = vsdeRAM.front()->strVbox; 1144 ULONG tt = (ULONG)RTStrToUInt64(memoryVBox.c_str()); 1145 rc = pNewMachine->COMSETTER(MemorySize)(tt); 1146 if (FAILED(rc)) throw rc; 1147 1148 /* VRAM */ 1149 /* Get the recommended VRAM for this guest OS type */ 1150 ULONG vramVBox; 1151 rc = osType->COMGETTER(RecommendedVRAM)(&vramVBox); 1152 if (FAILED(rc)) throw rc; 1153 1154 /* Set the VRAM */ 1155 rc = pNewMachine->COMSETTER(VRAMSize)(vramVBox); 1156 if (FAILED(rc)) throw rc; 1157 1158 /* I/O APIC: so far we have no setting for this. Enable it if we 1159 import a Windows VM because if if Windows was installed without IOAPIC, 1160 it will not mind finding an one later on, but if Windows was installed 1161 _with_ an IOAPIC, it will bluescreen if it's not found */ 1162 Bstr bstrFamilyId; 1163 rc = osType->COMGETTER(FamilyId)(bstrFamilyId.asOutParam()); 1164 if (FAILED(rc)) throw rc; 1165 1166 Utf8Str strFamilyId(bstrFamilyId); 1167 if (strFamilyId == "Windows") 1168 fEnableIOApic = true; 1169 1170 /* If IP-APIC should be enabled could be have different reasons. 1171 See CPU count & the Win test above. Here we enable it if it was 1172 previously requested. */ 1173 if (fEnableIOApic) 1174 { 1175 ComPtr<IBIOSSettings> pBIOSSettings; 1176 rc = pNewMachine->COMGETTER(BIOSSettings)(pBIOSSettings.asOutParam()); 1177 if (FAILED(rc)) throw rc; 1178 1179 rc = pBIOSSettings->COMSETTER(IOAPICEnabled)(TRUE); 1180 if (FAILED(rc)) throw rc; 1181 } 1182 1183 /* Audio Adapter */ 1184 std::list<VirtualSystemDescriptionEntry*> vsdeAudioAdapter = vsdescThis->findByType(VirtualSystemDescriptionType_SoundCard); 1185 /* @todo: we support one audio adapter only */ 1186 if (vsdeAudioAdapter.size() > 0) 1187 { 1188 const Utf8Str& audioAdapterVBox = vsdeAudioAdapter.front()->strVbox; 1189 if (audioAdapterVBox.compare("null", Utf8Str::CaseInsensitive) != 0) 1190 { 1191 uint32_t audio = RTStrToUInt32(audioAdapterVBox.c_str()); 1192 ComPtr<IAudioAdapter> audioAdapter; 1193 rc = pNewMachine->COMGETTER(AudioAdapter)(audioAdapter.asOutParam()); 1194 if (FAILED(rc)) throw rc; 1195 rc = audioAdapter->COMSETTER(Enabled)(true); 1196 if (FAILED(rc)) throw rc; 1197 rc = audioAdapter->COMSETTER(AudioController)(static_cast<AudioControllerType_T>(audio)); 1198 if (FAILED(rc)) throw rc; 1199 } 1200 } 1201 1202 #ifdef VBOX_WITH_USB 1203 /* USB Controller */ 1204 std::list<VirtualSystemDescriptionEntry*> vsdeUSBController = vsdescThis->findByType(VirtualSystemDescriptionType_USBController); 1205 // USB support is enabled if there's at least one such entry; to disable USB support, 1206 // the type of the USB item would have been changed to "ignore" 1207 bool fUSBEnabled = vsdeUSBController.size() > 0; 1208 1209 ComPtr<IUSBController> usbController; 1210 rc = pNewMachine->COMGETTER(USBController)(usbController.asOutParam()); 1211 if (FAILED(rc)) throw rc; 1212 rc = usbController->COMSETTER(Enabled)(fUSBEnabled); 1213 if (FAILED(rc)) throw rc; 1214 #endif /* VBOX_WITH_USB */ 1215 1216 /* Change the network adapters */ 1217 std::list<VirtualSystemDescriptionEntry*> vsdeNW = vsdescThis->findByType(VirtualSystemDescriptionType_NetworkAdapter); 1218 if (vsdeNW.size() == 0) 1219 { 1220 /* No network adapters, so we have to disable our default one */ 1221 ComPtr<INetworkAdapter> nwVBox; 1222 rc = pNewMachine->GetNetworkAdapter(0, nwVBox.asOutParam()); 1223 if (FAILED(rc)) throw rc; 1224 rc = nwVBox->COMSETTER(Enabled)(false); 1225 if (FAILED(rc)) throw rc; 1226 } 1113 // there are two ways in which we can create a vbox machine from OVF: 1114 // -- either this OVF was written by vbox 3.2 or later, in which case there is a <vbox:Machine> element 1115 // in the <VirtualSystem>; then the VirtualSystemDescription::Data has a settings::MachineConfigFile 1116 // with all the machine config pretty-parsed; 1117 // -- or this is an OVF from an older vbox or an external source, and then we need to translate the 1118 // VirtualSystemDescriptionEntry and do import work 1119 1120 // @todo r=dj make this selection configurable at run-time, and from the GUI as well 1121 1122 if (vsdescThis->m->pConfig) 1123 importVBoxMachine(*vsdescThis->m->pConfig, pNewMachine, stack); 1227 1124 else 1228 { 1229 list<VirtualSystemDescriptionEntry*>::const_iterator nwIt; 1230 /* Iterate through all network cards. We support 8 network adapters 1231 * at the maximum. (@todo: warn if there are more!) */ 1232 size_t a = 0; 1233 for (nwIt = vsdeNW.begin(); 1234 (nwIt != vsdeNW.end() && a < SchemaDefs::NetworkAdapterCount); 1235 ++nwIt, ++a) 1236 { 1237 const VirtualSystemDescriptionEntry* pvsys = *nwIt; 1238 1239 const Utf8Str &nwTypeVBox = pvsys->strVbox; 1240 uint32_t tt1 = RTStrToUInt32(nwTypeVBox.c_str()); 1241 ComPtr<INetworkAdapter> pNetworkAdapter; 1242 rc = pNewMachine->GetNetworkAdapter((ULONG)a, pNetworkAdapter.asOutParam()); 1243 if (FAILED(rc)) throw rc; 1244 /* Enable the network card & set the adapter type */ 1245 rc = pNetworkAdapter->COMSETTER(Enabled)(true); 1246 if (FAILED(rc)) throw rc; 1247 rc = pNetworkAdapter->COMSETTER(AdapterType)(static_cast<NetworkAdapterType_T>(tt1)); 1248 if (FAILED(rc)) throw rc; 1249 1250 // default is NAT; change to "bridged" if extra conf says so 1251 if (!pvsys->strExtraConfig.compare("type=Bridged", Utf8Str::CaseInsensitive)) 1252 { 1253 /* Attach to the right interface */ 1254 rc = pNetworkAdapter->AttachToBridgedInterface(); 1255 if (FAILED(rc)) throw rc; 1256 ComPtr<IHost> host; 1257 rc = mVirtualBox->COMGETTER(Host)(host.asOutParam()); 1258 if (FAILED(rc)) throw rc; 1259 com::SafeIfaceArray<IHostNetworkInterface> nwInterfaces; 1260 rc = host->COMGETTER(NetworkInterfaces)(ComSafeArrayAsOutParam(nwInterfaces)); 1261 if (FAILED(rc)) throw rc; 1262 /* We search for the first host network interface which 1263 * is usable for bridged networking */ 1264 for (size_t j = 0; 1265 j < nwInterfaces.size(); 1266 ++j) 1267 { 1268 HostNetworkInterfaceType_T itype; 1269 rc = nwInterfaces[j]->COMGETTER(InterfaceType)(&itype); 1270 if (FAILED(rc)) throw rc; 1271 if (itype == HostNetworkInterfaceType_Bridged) 1272 { 1273 Bstr name; 1274 rc = nwInterfaces[j]->COMGETTER(Name)(name.asOutParam()); 1275 if (FAILED(rc)) throw rc; 1276 /* Set the interface name to attach to */ 1277 pNetworkAdapter->COMSETTER(HostInterface)(name); 1278 if (FAILED(rc)) throw rc; 1279 break; 1280 } 1281 } 1282 } 1283 /* Next test for host only interfaces */ 1284 else if (!pvsys->strExtraConfig.compare("type=HostOnly", Utf8Str::CaseInsensitive)) 1285 { 1286 /* Attach to the right interface */ 1287 rc = pNetworkAdapter->AttachToHostOnlyInterface(); 1288 if (FAILED(rc)) throw rc; 1289 ComPtr<IHost> host; 1290 rc = mVirtualBox->COMGETTER(Host)(host.asOutParam()); 1291 if (FAILED(rc)) throw rc; 1292 com::SafeIfaceArray<IHostNetworkInterface> nwInterfaces; 1293 rc = host->COMGETTER(NetworkInterfaces)(ComSafeArrayAsOutParam(nwInterfaces)); 1294 if (FAILED(rc)) throw rc; 1295 /* We search for the first host network interface which 1296 * is usable for host only networking */ 1297 for (size_t j = 0; 1298 j < nwInterfaces.size(); 1299 ++j) 1300 { 1301 HostNetworkInterfaceType_T itype; 1302 rc = nwInterfaces[j]->COMGETTER(InterfaceType)(&itype); 1303 if (FAILED(rc)) throw rc; 1304 if (itype == HostNetworkInterfaceType_HostOnly) 1305 { 1306 Bstr name; 1307 rc = nwInterfaces[j]->COMGETTER(Name)(name.asOutParam()); 1308 if (FAILED(rc)) throw rc; 1309 /* Set the interface name to attach to */ 1310 pNetworkAdapter->COMSETTER(HostInterface)(name); 1311 if (FAILED(rc)) throw rc; 1312 break; 1313 } 1314 } 1315 } 1316 } 1317 } 1318 1319 /* Hard disk controller IDE */ 1320 std::list<VirtualSystemDescriptionEntry*> vsdeHDCIDE = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerIDE); 1321 if (vsdeHDCIDE.size() > 1) 1322 throw setError(VBOX_E_FILE_ERROR, 1323 tr("Too many IDE controllers in OVF; import facility only supports one")); 1324 if (vsdeHDCIDE.size() == 1) 1325 { 1326 ComPtr<IStorageController> pController; 1327 rc = pNewMachine->AddStorageController(Bstr("IDE Controller"), StorageBus_IDE, pController.asOutParam()); 1328 if (FAILED(rc)) throw rc; 1329 1330 const char *pcszIDEType = vsdeHDCIDE.front()->strVbox.c_str(); 1331 if (!strcmp(pcszIDEType, "PIIX3")) 1332 rc = pController->COMSETTER(ControllerType)(StorageControllerType_PIIX3); 1333 else if (!strcmp(pcszIDEType, "PIIX4")) 1334 rc = pController->COMSETTER(ControllerType)(StorageControllerType_PIIX4); 1335 else if (!strcmp(pcszIDEType, "ICH6")) 1336 rc = pController->COMSETTER(ControllerType)(StorageControllerType_ICH6); 1337 else 1338 throw setError(VBOX_E_FILE_ERROR, 1339 tr("Invalid IDE controller type \"%s\""), 1340 pcszIDEType); 1341 if (FAILED(rc)) throw rc; 1342 } 1343 #ifdef VBOX_WITH_AHCI 1344 /* Hard disk controller SATA */ 1345 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSATA = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSATA); 1346 if (vsdeHDCSATA.size() > 1) 1347 throw setError(VBOX_E_FILE_ERROR, 1348 tr("Too many SATA controllers in OVF; import facility only supports one")); 1349 if (vsdeHDCSATA.size() > 0) 1350 { 1351 ComPtr<IStorageController> pController; 1352 const Utf8Str &hdcVBox = vsdeHDCSATA.front()->strVbox; 1353 if (hdcVBox == "AHCI") 1354 { 1355 rc = pNewMachine->AddStorageController(Bstr("SATA Controller"), StorageBus_SATA, pController.asOutParam()); 1356 if (FAILED(rc)) throw rc; 1357 } 1358 else 1359 throw setError(VBOX_E_FILE_ERROR, 1360 tr("Invalid SATA controller type \"%s\""), 1361 hdcVBox.c_str()); 1362 } 1363 #endif /* VBOX_WITH_AHCI */ 1364 1365 #ifdef VBOX_WITH_LSILOGIC 1366 /* Hard disk controller SCSI */ 1367 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSCSI = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSCSI); 1368 if (vsdeHDCSCSI.size() > 1) 1369 throw setError(VBOX_E_FILE_ERROR, 1370 tr("Too many SCSI controllers in OVF; import facility only supports one")); 1371 if (vsdeHDCSCSI.size() > 0) 1372 { 1373 ComPtr<IStorageController> pController; 1374 StorageControllerType_T controllerType; 1375 const Utf8Str &hdcVBox = vsdeHDCSCSI.front()->strVbox; 1376 if (hdcVBox == "LsiLogic") 1377 controllerType = StorageControllerType_LsiLogic; 1378 else if (hdcVBox == "BusLogic") 1379 controllerType = StorageControllerType_BusLogic; 1380 else 1381 throw setError(VBOX_E_FILE_ERROR, 1382 tr("Invalid SCSI controller type \"%s\""), 1383 hdcVBox.c_str()); 1384 1385 rc = pNewMachine->AddStorageController(Bstr("SCSI Controller"), StorageBus_SCSI, pController.asOutParam()); 1386 if (FAILED(rc)) throw rc; 1387 rc = pController->COMSETTER(ControllerType)(controllerType); 1388 if (FAILED(rc)) throw rc; 1389 } 1390 #endif /* VBOX_WITH_LSILOGIC */ 1391 1392 /* Now its time to register the machine before we add any hard disks */ 1393 rc = mVirtualBox->RegisterMachine(pNewMachine); 1394 if (FAILED(rc)) throw rc; 1395 1396 Bstr bstrNewMachineId; 1397 rc = pNewMachine->COMGETTER(Id)(bstrNewMachineId.asOutParam()); 1398 if (FAILED(rc)) throw rc; 1399 1400 // store new machine for roll-back in case of errors 1401 llMachinesRegistered.push_back(bstrNewMachineId); 1402 1403 // Add floppies and CD-ROMs to the appropriate controllers. 1404 std::list<VirtualSystemDescriptionEntry*> vsdeFloppy = vsdescThis->findByType(VirtualSystemDescriptionType_Floppy); 1405 if (vsdeFloppy.size() > 1) 1406 throw setError(VBOX_E_FILE_ERROR, 1407 tr("Too many floppy controllers in OVF; import facility only supports one")); 1408 std::list<VirtualSystemDescriptionEntry*> vsdeCDROM = vsdescThis->findByType(VirtualSystemDescriptionType_CDROM); 1409 if ( (vsdeFloppy.size() > 0) 1410 || (vsdeCDROM.size() > 0) 1411 ) 1412 { 1413 // If there's an error here we need to close the session, so 1414 // we need another try/catch block. 1415 1416 try 1417 { 1418 /* In order to attach things we need to open a session 1419 * for the new machine */ 1420 rc = mVirtualBox->OpenSession(session, bstrNewMachineId); 1421 if (FAILED(rc)) throw rc; 1422 fSessionOpen = true; 1423 1424 ComPtr<IMachine> sMachine; 1425 rc = session->COMGETTER(Machine)(sMachine.asOutParam()); 1426 if (FAILED(rc)) throw rc; 1427 1428 // floppy first 1429 if (vsdeFloppy.size() == 1) 1430 { 1431 ComPtr<IStorageController> pController; 1432 rc = sMachine->AddStorageController(Bstr("Floppy Controller"), StorageBus_Floppy, pController.asOutParam()); 1433 if (FAILED(rc)) throw rc; 1434 1435 Bstr bstrName; 1436 rc = pController->COMGETTER(Name)(bstrName.asOutParam()); 1437 if (FAILED(rc)) throw rc; 1438 1439 // this is for rollback later 1440 MyHardDiskAttachment mhda; 1441 mhda.bstrUuid = bstrNewMachineId; 1442 mhda.pMachine = pNewMachine; 1443 mhda.controllerType = bstrName; 1444 mhda.lChannel = 0; 1445 mhda.lDevice = 0; 1446 1447 Log(("Attaching floppy\n")); 1448 1449 rc = sMachine->AttachDevice(mhda.controllerType, 1450 mhda.lChannel, 1451 mhda.lDevice, 1452 DeviceType_Floppy, 1453 NULL); 1454 if (FAILED(rc)) throw rc; 1455 1456 llHardDiskAttachments.push_back(mhda); 1457 } 1458 1459 1460 // CD-ROMs next 1461 for (std::list<VirtualSystemDescriptionEntry*>::const_iterator jt = vsdeCDROM.begin(); 1462 jt != vsdeCDROM.end(); 1463 ++jt) 1464 { 1465 // for now always attach to secondary master on IDE controller; 1466 // there seems to be no useful information in OVF where else to 1467 // attach jt (@todo test with latest versions of OVF software) 1468 1469 // find the IDE controller 1470 const ovf::HardDiskController *pController = NULL; 1471 for (ovf::ControllersMap::const_iterator kt = vsysThis.mapControllers.begin(); 1472 kt != vsysThis.mapControllers.end(); 1473 ++kt) 1474 { 1475 if (kt->second.system == ovf::HardDiskController::IDE) 1476 { 1477 pController = &kt->second; 1478 } 1479 } 1480 1481 if (!pController) 1482 throw setError(VBOX_E_FILE_ERROR, 1483 tr("OVF wants a CD-ROM drive but cannot find IDE controller, which is required in this version of VirtualBox")); 1484 1485 // this is for rollback later 1486 MyHardDiskAttachment mhda; 1487 mhda.bstrUuid = bstrNewMachineId; 1488 mhda.pMachine = pNewMachine; 1489 1490 convertDiskAttachmentValues(*pController, 1491 2, // interpreted as secondary master 1492 mhda.controllerType, // Bstr 1493 mhda.lChannel, 1494 mhda.lDevice); 1495 1496 Log(("Attaching CD-ROM to channel %d on device %d\n", mhda.lChannel, mhda.lDevice)); 1497 1498 rc = sMachine->AttachDevice(mhda.controllerType, 1499 mhda.lChannel, 1500 mhda.lDevice, 1501 DeviceType_DVD, 1502 NULL); 1503 if (FAILED(rc)) throw rc; 1504 1505 llHardDiskAttachments.push_back(mhda); 1506 } // end for (itHD = avsdeHDs.begin(); 1507 1508 rc = sMachine->SaveSettings(); 1509 if (FAILED(rc)) throw rc; 1510 1511 // only now that we're done with all disks, close the session 1512 rc = session->Close(); 1513 if (FAILED(rc)) throw rc; 1514 fSessionOpen = false; 1515 } 1516 catch(HRESULT /* aRC */) 1517 { 1518 if (fSessionOpen) 1519 session->Close(); 1520 1521 throw; 1522 } 1523 } 1524 1525 /* Create the hard disks & connect them to the appropriate controllers. */ 1526 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage); 1527 if (avsdeHDs.size() > 0) 1528 { 1529 // If there's an error here we need to close the session, so 1530 // we need another try/catch block. 1531 ComPtr<IMedium> srcHdVBox; 1532 bool fSourceHdNeedsClosing = false; 1533 1534 try 1535 { 1536 /* In order to attach hard disks we need to open a session 1537 * for the new machine */ 1538 rc = mVirtualBox->OpenSession(session, bstrNewMachineId); 1539 if (FAILED(rc)) throw rc; 1540 fSessionOpen = true; 1541 1542 /* The disk image has to be on the same place as the OVF file. So 1543 * strip the filename out of the full file path. */ 1544 Utf8Str strSrcDir(locInfo.strPath); 1545 strSrcDir.stripFilename(); 1546 1547 /* Iterate over all given disk images */ 1548 list<VirtualSystemDescriptionEntry*>::const_iterator itHD; 1549 for (itHD = avsdeHDs.begin(); 1550 itHD != avsdeHDs.end(); 1551 ++itHD) 1552 { 1553 VirtualSystemDescriptionEntry *vsdeHD = *itHD; 1554 1555 /* Check if the destination file exists already or the 1556 * destination path is empty. */ 1557 if ( vsdeHD->strVbox.isEmpty() 1558 || RTPathExists(vsdeHD->strVbox.c_str()) 1559 ) 1560 /* This isn't allowed */ 1561 throw setError(VBOX_E_FILE_ERROR, 1562 tr("Destination file '%s' exists", 1563 vsdeHD->strVbox.c_str())); 1564 1565 /* Find the disk from the OVF's disk list */ 1566 ovf::DiskImagesMap::const_iterator itDiskImage = reader.m_mapDisks.find(vsdeHD->strRef); 1567 /* vsdeHD->strRef contains the disk identifier (e.g. "vmdisk1"), which should exist 1568 in the virtual system's disks map under that ID and also in the global images map. */ 1569 ovf::VirtualDisksMap::const_iterator itVirtualDisk = vsysThis.mapVirtualDisks.find(vsdeHD->strRef); 1570 1571 if ( itDiskImage == reader.m_mapDisks.end() 1572 || itVirtualDisk == vsysThis.mapVirtualDisks.end() 1573 ) 1574 throw setError(E_FAIL, 1575 tr("Internal inconsistency looking up disk images.")); 1576 1577 const ovf::DiskImage &di = itDiskImage->second; 1578 const ovf::VirtualDisk &vd = itVirtualDisk->second; 1579 1580 /* Make sure all target directories exists */ 1581 rc = VirtualBox::ensureFilePathExists(vsdeHD->strVbox.c_str()); 1582 if (FAILED(rc)) 1583 throw rc; 1584 1585 // subprogress object for hard disk 1586 ComPtr<IProgress> pProgress2; 1587 1588 ComPtr<IMedium> dstHdVBox; 1589 /* If strHref is empty we have to create a new file */ 1590 if (di.strHref.isEmpty()) 1591 { 1592 /* Which format to use? */ 1593 Bstr srcFormat = L"VDI"; 1594 if ( di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#sparse", Utf8Str::CaseInsensitive) 1595 || di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive)) 1596 srcFormat = L"VMDK"; 1597 /* Create an empty hard disk */ 1598 rc = mVirtualBox->CreateHardDisk(srcFormat, Bstr(vsdeHD->strVbox), dstHdVBox.asOutParam()); 1599 if (FAILED(rc)) throw rc; 1600 1601 /* Create a dynamic growing disk image with the given capacity */ 1602 rc = dstHdVBox->CreateBaseStorage(di.iCapacity / _1M, MediumVariant_Standard, pProgress2.asOutParam()); 1603 if (FAILED(rc)) throw rc; 1604 1605 /* Advance to the next operation */ 1606 if (!pProgress.isNull()) 1607 pProgress->SetNextOperation(BstrFmt(tr("Creating virtual disk image '%s'"), vsdeHD->strVbox.c_str()), 1608 vsdeHD->ulSizeMB); // operation's weight, as set up with the IProgress originally 1609 } 1610 else 1611 { 1612 /* Construct the source file path */ 1613 Utf8StrFmt strSrcFilePath("%s%c%s", strSrcDir.c_str(), RTPATH_DELIMITER, di.strHref.c_str()); 1614 /* Check if the source file exists */ 1615 if (!RTPathExists(strSrcFilePath.c_str())) 1616 /* This isn't allowed */ 1617 throw setError(VBOX_E_FILE_ERROR, 1618 tr("Source virtual disk image file '%s' doesn't exist"), 1619 strSrcFilePath.c_str()); 1620 1621 /* Clone the disk image (this is necessary cause the id has 1622 * to be recreated for the case the same hard disk is 1623 * attached already from a previous import) */ 1624 1625 /* First open the existing disk image */ 1626 rc = mVirtualBox->OpenHardDisk(Bstr(strSrcFilePath), 1627 AccessMode_ReadOnly, 1628 false, 1629 NULL, 1630 false, 1631 NULL, 1632 srcHdVBox.asOutParam()); 1633 if (FAILED(rc)) throw rc; 1634 fSourceHdNeedsClosing = true; 1635 1636 /* We need the format description of the source disk image */ 1637 Bstr srcFormat; 1638 rc = srcHdVBox->COMGETTER(Format)(srcFormat.asOutParam()); 1639 if (FAILED(rc)) throw rc; 1640 /* Create a new hard disk interface for the destination disk image */ 1641 rc = mVirtualBox->CreateHardDisk(srcFormat, Bstr(vsdeHD->strVbox), dstHdVBox.asOutParam()); 1642 if (FAILED(rc)) throw rc; 1643 /* Clone the source disk image */ 1644 rc = srcHdVBox->CloneTo(dstHdVBox, MediumVariant_Standard, NULL, pProgress2.asOutParam()); 1645 if (FAILED(rc)) throw rc; 1646 1647 /* Advance to the next operation */ 1648 if (!pProgress.isNull()) 1649 pProgress->SetNextOperation(BstrFmt(tr("Importing virtual disk image '%s'"), strSrcFilePath.c_str()), 1650 vsdeHD->ulSizeMB); // operation's weight, as set up with the IProgress originally); 1651 } 1652 1653 // now wait for the background disk operation to complete; this throws HRESULTs on error 1654 waitForAsyncProgress(pProgress, pProgress2); 1655 1656 if (fSourceHdNeedsClosing) 1657 { 1658 rc = srcHdVBox->Close(); 1659 if (FAILED(rc)) throw rc; 1660 fSourceHdNeedsClosing = false; 1661 } 1662 1663 llHardDisksCreated.push_back(dstHdVBox); 1664 /* Now use the new uuid to attach the disk image to our new machine */ 1665 ComPtr<IMachine> sMachine; 1666 rc = session->COMGETTER(Machine)(sMachine.asOutParam()); 1667 if (FAILED(rc)) throw rc; 1668 Bstr hdId; 1669 rc = dstHdVBox->COMGETTER(Id)(hdId.asOutParam()); 1670 if (FAILED(rc)) throw rc; 1671 1672 /* For now we assume we have one controller of every type only */ 1673 ovf::HardDiskController hdc = (*vsysThis.mapControllers.find(vd.idController)).second; 1674 1675 // this is for rollback later 1676 MyHardDiskAttachment mhda; 1677 mhda.bstrUuid = bstrNewMachineId; 1678 mhda.pMachine = pNewMachine; 1679 1680 convertDiskAttachmentValues(hdc, 1681 vd.ulAddressOnParent, 1682 mhda.controllerType, // Bstr 1683 mhda.lChannel, 1684 mhda.lDevice); 1685 1686 Log(("Attaching disk %s to channel %d on device %d\n", vsdeHD->strVbox.c_str(), mhda.lChannel, mhda.lDevice)); 1687 1688 rc = sMachine->AttachDevice(mhda.controllerType, 1689 mhda.lChannel, 1690 mhda.lDevice, 1691 DeviceType_HardDisk, 1692 hdId); 1693 if (FAILED(rc)) throw rc; 1694 1695 llHardDiskAttachments.push_back(mhda); 1696 1697 rc = sMachine->SaveSettings(); 1698 if (FAILED(rc)) throw rc; 1699 } // end for (itHD = avsdeHDs.begin(); 1700 1701 // only now that we're done with all disks, close the session 1702 rc = session->Close(); 1703 if (FAILED(rc)) throw rc; 1704 fSessionOpen = false; 1705 } 1706 catch(HRESULT /* aRC */) 1707 { 1708 if (fSourceHdNeedsClosing) 1709 srcHdVBox->Close(); 1710 1711 if (fSessionOpen) 1712 session->Close(); 1713 1714 throw; 1715 } 1716 } 1125 importMachineGeneric(vsysThis, vsdescThis, pNewMachine, stack); 1717 1126 } 1718 1127 catch(HRESULT aRC) … … 1735 1144 // detach all hard disks from all machines we created 1736 1145 list<MyHardDiskAttachment>::iterator itM; 1737 for (itM = llHardDiskAttachments.begin();1738 itM != llHardDiskAttachments.end();1146 for (itM = stack.llHardDiskAttachments.begin(); 1147 itM != stack.llHardDiskAttachments.end(); 1739 1148 ++itM) 1740 1149 { 1741 1150 const MyHardDiskAttachment &mhda = *itM; 1742 1151 Bstr bstrUuid(mhda.bstrUuid); // make a copy, Windows can't handle const Bstr 1743 rc2 = mVirtualBox->OpenSession(s ession, bstrUuid);1152 rc2 = mVirtualBox->OpenSession(stack.pSession, bstrUuid); 1744 1153 if (SUCCEEDED(rc2)) 1745 1154 { 1746 1155 ComPtr<IMachine> sMachine; 1747 rc2 = s ession->COMGETTER(Machine)(sMachine.asOutParam());1156 rc2 = stack.pSession->COMGETTER(Machine)(sMachine.asOutParam()); 1748 1157 if (SUCCEEDED(rc2)) 1749 1158 { … … 1751 1160 rc2 = sMachine->SaveSettings(); 1752 1161 } 1753 s ession->Close();1162 stack.pSession->Close(); 1754 1163 } 1755 1164 } … … 1757 1166 // now clean up all hard disks we created 1758 1167 list< ComPtr<IMedium> >::iterator itHD; 1759 for (itHD = llHardDisksCreated.begin();1760 itHD != llHardDisksCreated.end();1168 for (itHD = stack.llHardDisksCreated.begin(); 1169 itHD != stack.llHardDisksCreated.end(); 1761 1170 ++itHD) 1762 1171 { … … 1769 1178 // finally, deregister and remove all machines 1770 1179 list<Bstr>::iterator itID; 1771 for (itID = llMachinesRegistered.begin();1772 itID != llMachinesRegistered.end();1180 for (itID = stack.llMachinesRegistered.begin(); 1181 itID != stack.llMachinesRegistered.end(); 1773 1182 ++itID) 1774 1183 { … … 1789 1198 1790 1199 return rc; 1200 } 1201 1202 /** 1203 * Imports one OVF virtual system (described by the given ovf::VirtualSystem and VirtualSystemDescription) 1204 * into VirtualBox by creating an IMachine instance, which is returned. 1205 * 1206 * This throws HRESULT error codes for anything that goes wrong, in which case the caller must clean 1207 * up any leftovers from this function. For this, the given ImportStack instance has received information 1208 * about what needs cleaning up (to support rollback). 1209 * 1210 * @param locInfo 1211 * @param vsysThis 1212 * @param vsdescThis 1213 * @param pNewMachine 1214 * @param stack 1215 */ 1216 void Appliance::importMachineGeneric(const ovf::VirtualSystem &vsysThis, 1217 ComObjPtr<VirtualSystemDescription> &vsdescThis, 1218 ComPtr<IMachine> &pNewMachine, 1219 ImportStack &stack) 1220 { 1221 /* Guest OS type */ 1222 std::list<VirtualSystemDescriptionEntry*> vsdeOS; 1223 vsdeOS = vsdescThis->findByType(VirtualSystemDescriptionType_OS); 1224 if (vsdeOS.size() < 1) 1225 throw setError(VBOX_E_FILE_ERROR, 1226 tr("Missing guest OS type")); 1227 const Utf8Str &strOsTypeVBox = vsdeOS.front()->strVbox; 1228 1229 /* Now that we know the base system get our internal defaults based on that. */ 1230 ComPtr<IGuestOSType> osType; 1231 HRESULT rc = mVirtualBox->GetGuestOSType(Bstr(strOsTypeVBox), osType.asOutParam()); 1232 if (FAILED(rc)) throw rc; 1233 1234 /* Create the machine */ 1235 /* First get the name */ 1236 std::list<VirtualSystemDescriptionEntry*> vsdeName = vsdescThis->findByType(VirtualSystemDescriptionType_Name); 1237 if (vsdeName.size() < 1) 1238 throw setError(VBOX_E_FILE_ERROR, 1239 tr("Missing VM name")); 1240 const Utf8Str &strNameVBox = vsdeName.front()->strVbox; 1241 rc = mVirtualBox->CreateMachine(Bstr(strNameVBox), 1242 Bstr(strOsTypeVBox), 1243 NULL, 1244 NULL, 1245 FALSE, 1246 pNewMachine.asOutParam()); 1247 if (FAILED(rc)) throw rc; 1248 1249 // and the description 1250 std::list<VirtualSystemDescriptionEntry*> vsdeDescription = vsdescThis->findByType(VirtualSystemDescriptionType_Description); 1251 if (vsdeDescription.size()) 1252 { 1253 const Utf8Str &strDescription = vsdeDescription.front()->strVbox; 1254 rc = pNewMachine->COMSETTER(Description)(Bstr(strDescription)); 1255 if (FAILED(rc)) throw rc; 1256 } 1257 1258 /* CPU count */ 1259 std::list<VirtualSystemDescriptionEntry*> vsdeCPU = vsdescThis->findByType(VirtualSystemDescriptionType_CPU); 1260 ComAssertMsgThrow(vsdeCPU.size() == 1, ("CPU count missing"), E_FAIL); 1261 const Utf8Str &cpuVBox = vsdeCPU.front()->strVbox; 1262 ULONG tmpCount = (ULONG)RTStrToUInt64(cpuVBox.c_str()); 1263 rc = pNewMachine->COMSETTER(CPUCount)(tmpCount); 1264 if (FAILED(rc)) throw rc; 1265 bool fEnableIOApic = false; 1266 /* We need HWVirt & IO-APIC if more than one CPU is requested */ 1267 if (tmpCount > 1) 1268 { 1269 rc = pNewMachine->SetHWVirtExProperty(HWVirtExPropertyType_Enabled, TRUE); 1270 if (FAILED(rc)) throw rc; 1271 1272 fEnableIOApic = true; 1273 } 1274 1275 /* RAM */ 1276 std::list<VirtualSystemDescriptionEntry*> vsdeRAM = vsdescThis->findByType(VirtualSystemDescriptionType_Memory); 1277 ComAssertMsgThrow(vsdeRAM.size() == 1, ("RAM size missing"), E_FAIL); 1278 const Utf8Str &memoryVBox = vsdeRAM.front()->strVbox; 1279 ULONG tt = (ULONG)RTStrToUInt64(memoryVBox.c_str()); 1280 rc = pNewMachine->COMSETTER(MemorySize)(tt); 1281 if (FAILED(rc)) throw rc; 1282 1283 /* VRAM */ 1284 /* Get the recommended VRAM for this guest OS type */ 1285 ULONG vramVBox; 1286 rc = osType->COMGETTER(RecommendedVRAM)(&vramVBox); 1287 if (FAILED(rc)) throw rc; 1288 1289 /* Set the VRAM */ 1290 rc = pNewMachine->COMSETTER(VRAMSize)(vramVBox); 1291 if (FAILED(rc)) throw rc; 1292 1293 /* I/O APIC: so far we have no setting for this. Enable it if we 1294 import a Windows VM because if if Windows was installed without IOAPIC, 1295 it will not mind finding an one later on, but if Windows was installed 1296 _with_ an IOAPIC, it will bluescreen if it's not found */ 1297 Bstr bstrFamilyId; 1298 rc = osType->COMGETTER(FamilyId)(bstrFamilyId.asOutParam()); 1299 if (FAILED(rc)) throw rc; 1300 1301 Utf8Str strFamilyId(bstrFamilyId); 1302 if (strFamilyId == "Windows") 1303 fEnableIOApic = true; 1304 1305 /* If IP-APIC should be enabled could be have different reasons. 1306 See CPU count & the Win test above. Here we enable it if it was 1307 previously requested. */ 1308 if (fEnableIOApic) 1309 { 1310 ComPtr<IBIOSSettings> pBIOSSettings; 1311 rc = pNewMachine->COMGETTER(BIOSSettings)(pBIOSSettings.asOutParam()); 1312 if (FAILED(rc)) throw rc; 1313 1314 rc = pBIOSSettings->COMSETTER(IOAPICEnabled)(TRUE); 1315 if (FAILED(rc)) throw rc; 1316 } 1317 1318 /* Audio Adapter */ 1319 std::list<VirtualSystemDescriptionEntry*> vsdeAudioAdapter = vsdescThis->findByType(VirtualSystemDescriptionType_SoundCard); 1320 /* @todo: we support one audio adapter only */ 1321 if (vsdeAudioAdapter.size() > 0) 1322 { 1323 const Utf8Str& audioAdapterVBox = vsdeAudioAdapter.front()->strVbox; 1324 if (audioAdapterVBox.compare("null", Utf8Str::CaseInsensitive) != 0) 1325 { 1326 uint32_t audio = RTStrToUInt32(audioAdapterVBox.c_str()); 1327 ComPtr<IAudioAdapter> audioAdapter; 1328 rc = pNewMachine->COMGETTER(AudioAdapter)(audioAdapter.asOutParam()); 1329 if (FAILED(rc)) throw rc; 1330 rc = audioAdapter->COMSETTER(Enabled)(true); 1331 if (FAILED(rc)) throw rc; 1332 rc = audioAdapter->COMSETTER(AudioController)(static_cast<AudioControllerType_T>(audio)); 1333 if (FAILED(rc)) throw rc; 1334 } 1335 } 1336 1337 #ifdef VBOX_WITH_USB 1338 /* USB Controller */ 1339 std::list<VirtualSystemDescriptionEntry*> vsdeUSBController = vsdescThis->findByType(VirtualSystemDescriptionType_USBController); 1340 // USB support is enabled if there's at least one such entry; to disable USB support, 1341 // the type of the USB item would have been changed to "ignore" 1342 bool fUSBEnabled = vsdeUSBController.size() > 0; 1343 1344 ComPtr<IUSBController> usbController; 1345 rc = pNewMachine->COMGETTER(USBController)(usbController.asOutParam()); 1346 if (FAILED(rc)) throw rc; 1347 rc = usbController->COMSETTER(Enabled)(fUSBEnabled); 1348 if (FAILED(rc)) throw rc; 1349 #endif /* VBOX_WITH_USB */ 1350 1351 /* Change the network adapters */ 1352 std::list<VirtualSystemDescriptionEntry*> vsdeNW = vsdescThis->findByType(VirtualSystemDescriptionType_NetworkAdapter); 1353 if (vsdeNW.size() == 0) 1354 { 1355 /* No network adapters, so we have to disable our default one */ 1356 ComPtr<INetworkAdapter> nwVBox; 1357 rc = pNewMachine->GetNetworkAdapter(0, nwVBox.asOutParam()); 1358 if (FAILED(rc)) throw rc; 1359 rc = nwVBox->COMSETTER(Enabled)(false); 1360 if (FAILED(rc)) throw rc; 1361 } 1362 else 1363 { 1364 list<VirtualSystemDescriptionEntry*>::const_iterator nwIt; 1365 /* Iterate through all network cards. We support 8 network adapters 1366 * at the maximum. (@todo: warn if there are more!) */ 1367 size_t a = 0; 1368 for (nwIt = vsdeNW.begin(); 1369 (nwIt != vsdeNW.end() && a < SchemaDefs::NetworkAdapterCount); 1370 ++nwIt, ++a) 1371 { 1372 const VirtualSystemDescriptionEntry* pvsys = *nwIt; 1373 1374 const Utf8Str &nwTypeVBox = pvsys->strVbox; 1375 uint32_t tt1 = RTStrToUInt32(nwTypeVBox.c_str()); 1376 ComPtr<INetworkAdapter> pNetworkAdapter; 1377 rc = pNewMachine->GetNetworkAdapter((ULONG)a, pNetworkAdapter.asOutParam()); 1378 if (FAILED(rc)) throw rc; 1379 /* Enable the network card & set the adapter type */ 1380 rc = pNetworkAdapter->COMSETTER(Enabled)(true); 1381 if (FAILED(rc)) throw rc; 1382 rc = pNetworkAdapter->COMSETTER(AdapterType)(static_cast<NetworkAdapterType_T>(tt1)); 1383 if (FAILED(rc)) throw rc; 1384 1385 // default is NAT; change to "bridged" if extra conf says so 1386 if (!pvsys->strExtraConfig.compare("type=Bridged", Utf8Str::CaseInsensitive)) 1387 { 1388 /* Attach to the right interface */ 1389 rc = pNetworkAdapter->AttachToBridgedInterface(); 1390 if (FAILED(rc)) throw rc; 1391 ComPtr<IHost> host; 1392 rc = mVirtualBox->COMGETTER(Host)(host.asOutParam()); 1393 if (FAILED(rc)) throw rc; 1394 com::SafeIfaceArray<IHostNetworkInterface> nwInterfaces; 1395 rc = host->COMGETTER(NetworkInterfaces)(ComSafeArrayAsOutParam(nwInterfaces)); 1396 if (FAILED(rc)) throw rc; 1397 /* We search for the first host network interface which 1398 * is usable for bridged networking */ 1399 for (size_t j = 0; 1400 j < nwInterfaces.size(); 1401 ++j) 1402 { 1403 HostNetworkInterfaceType_T itype; 1404 rc = nwInterfaces[j]->COMGETTER(InterfaceType)(&itype); 1405 if (FAILED(rc)) throw rc; 1406 if (itype == HostNetworkInterfaceType_Bridged) 1407 { 1408 Bstr name; 1409 rc = nwInterfaces[j]->COMGETTER(Name)(name.asOutParam()); 1410 if (FAILED(rc)) throw rc; 1411 /* Set the interface name to attach to */ 1412 pNetworkAdapter->COMSETTER(HostInterface)(name); 1413 if (FAILED(rc)) throw rc; 1414 break; 1415 } 1416 } 1417 } 1418 /* Next test for host only interfaces */ 1419 else if (!pvsys->strExtraConfig.compare("type=HostOnly", Utf8Str::CaseInsensitive)) 1420 { 1421 /* Attach to the right interface */ 1422 rc = pNetworkAdapter->AttachToHostOnlyInterface(); 1423 if (FAILED(rc)) throw rc; 1424 ComPtr<IHost> host; 1425 rc = mVirtualBox->COMGETTER(Host)(host.asOutParam()); 1426 if (FAILED(rc)) throw rc; 1427 com::SafeIfaceArray<IHostNetworkInterface> nwInterfaces; 1428 rc = host->COMGETTER(NetworkInterfaces)(ComSafeArrayAsOutParam(nwInterfaces)); 1429 if (FAILED(rc)) throw rc; 1430 /* We search for the first host network interface which 1431 * is usable for host only networking */ 1432 for (size_t j = 0; 1433 j < nwInterfaces.size(); 1434 ++j) 1435 { 1436 HostNetworkInterfaceType_T itype; 1437 rc = nwInterfaces[j]->COMGETTER(InterfaceType)(&itype); 1438 if (FAILED(rc)) throw rc; 1439 if (itype == HostNetworkInterfaceType_HostOnly) 1440 { 1441 Bstr name; 1442 rc = nwInterfaces[j]->COMGETTER(Name)(name.asOutParam()); 1443 if (FAILED(rc)) throw rc; 1444 /* Set the interface name to attach to */ 1445 pNetworkAdapter->COMSETTER(HostInterface)(name); 1446 if (FAILED(rc)) throw rc; 1447 break; 1448 } 1449 } 1450 } 1451 } 1452 } 1453 1454 /* Hard disk controller IDE */ 1455 std::list<VirtualSystemDescriptionEntry*> vsdeHDCIDE = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerIDE); 1456 if (vsdeHDCIDE.size() > 1) 1457 throw setError(VBOX_E_FILE_ERROR, 1458 tr("Too many IDE controllers in OVF; import facility only supports one")); 1459 if (vsdeHDCIDE.size() == 1) 1460 { 1461 ComPtr<IStorageController> pController; 1462 rc = pNewMachine->AddStorageController(Bstr("IDE Controller"), StorageBus_IDE, pController.asOutParam()); 1463 if (FAILED(rc)) throw rc; 1464 1465 const char *pcszIDEType = vsdeHDCIDE.front()->strVbox.c_str(); 1466 if (!strcmp(pcszIDEType, "PIIX3")) 1467 rc = pController->COMSETTER(ControllerType)(StorageControllerType_PIIX3); 1468 else if (!strcmp(pcszIDEType, "PIIX4")) 1469 rc = pController->COMSETTER(ControllerType)(StorageControllerType_PIIX4); 1470 else if (!strcmp(pcszIDEType, "ICH6")) 1471 rc = pController->COMSETTER(ControllerType)(StorageControllerType_ICH6); 1472 else 1473 throw setError(VBOX_E_FILE_ERROR, 1474 tr("Invalid IDE controller type \"%s\""), 1475 pcszIDEType); 1476 if (FAILED(rc)) throw rc; 1477 } 1478 #ifdef VBOX_WITH_AHCI 1479 /* Hard disk controller SATA */ 1480 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSATA = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSATA); 1481 if (vsdeHDCSATA.size() > 1) 1482 throw setError(VBOX_E_FILE_ERROR, 1483 tr("Too many SATA controllers in OVF; import facility only supports one")); 1484 if (vsdeHDCSATA.size() > 0) 1485 { 1486 ComPtr<IStorageController> pController; 1487 const Utf8Str &hdcVBox = vsdeHDCSATA.front()->strVbox; 1488 if (hdcVBox == "AHCI") 1489 { 1490 rc = pNewMachine->AddStorageController(Bstr("SATA Controller"), StorageBus_SATA, pController.asOutParam()); 1491 if (FAILED(rc)) throw rc; 1492 } 1493 else 1494 throw setError(VBOX_E_FILE_ERROR, 1495 tr("Invalid SATA controller type \"%s\""), 1496 hdcVBox.c_str()); 1497 } 1498 #endif /* VBOX_WITH_AHCI */ 1499 1500 #ifdef VBOX_WITH_LSILOGIC 1501 /* Hard disk controller SCSI */ 1502 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSCSI = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSCSI); 1503 if (vsdeHDCSCSI.size() > 1) 1504 throw setError(VBOX_E_FILE_ERROR, 1505 tr("Too many SCSI controllers in OVF; import facility only supports one")); 1506 if (vsdeHDCSCSI.size() > 0) 1507 { 1508 ComPtr<IStorageController> pController; 1509 StorageControllerType_T controllerType; 1510 const Utf8Str &hdcVBox = vsdeHDCSCSI.front()->strVbox; 1511 if (hdcVBox == "LsiLogic") 1512 controllerType = StorageControllerType_LsiLogic; 1513 else if (hdcVBox == "BusLogic") 1514 controllerType = StorageControllerType_BusLogic; 1515 else 1516 throw setError(VBOX_E_FILE_ERROR, 1517 tr("Invalid SCSI controller type \"%s\""), 1518 hdcVBox.c_str()); 1519 1520 rc = pNewMachine->AddStorageController(Bstr("SCSI Controller"), StorageBus_SCSI, pController.asOutParam()); 1521 if (FAILED(rc)) throw rc; 1522 rc = pController->COMSETTER(ControllerType)(controllerType); 1523 if (FAILED(rc)) throw rc; 1524 } 1525 #endif /* VBOX_WITH_LSILOGIC */ 1526 1527 /* Now its time to register the machine before we add any hard disks */ 1528 rc = mVirtualBox->RegisterMachine(pNewMachine); 1529 if (FAILED(rc)) throw rc; 1530 1531 Bstr bstrNewMachineId; 1532 rc = pNewMachine->COMGETTER(Id)(bstrNewMachineId.asOutParam()); 1533 if (FAILED(rc)) throw rc; 1534 1535 // store new machine for roll-back in case of errors 1536 stack.llMachinesRegistered.push_back(bstrNewMachineId); 1537 1538 // Add floppies and CD-ROMs to the appropriate controllers. 1539 std::list<VirtualSystemDescriptionEntry*> vsdeFloppy = vsdescThis->findByType(VirtualSystemDescriptionType_Floppy); 1540 if (vsdeFloppy.size() > 1) 1541 throw setError(VBOX_E_FILE_ERROR, 1542 tr("Too many floppy controllers in OVF; import facility only supports one")); 1543 std::list<VirtualSystemDescriptionEntry*> vsdeCDROM = vsdescThis->findByType(VirtualSystemDescriptionType_CDROM); 1544 if ( (vsdeFloppy.size() > 0) 1545 || (vsdeCDROM.size() > 0) 1546 ) 1547 { 1548 // If there's an error here we need to close the session, so 1549 // we need another try/catch block. 1550 1551 try 1552 { 1553 /* In order to attach things we need to open a session 1554 * for the new machine */ 1555 rc = mVirtualBox->OpenSession(stack.pSession, bstrNewMachineId); 1556 if (FAILED(rc)) throw rc; 1557 stack.fSessionOpen = true; 1558 1559 ComPtr<IMachine> sMachine; 1560 rc = stack.pSession->COMGETTER(Machine)(sMachine.asOutParam()); 1561 if (FAILED(rc)) throw rc; 1562 1563 // floppy first 1564 if (vsdeFloppy.size() == 1) 1565 { 1566 ComPtr<IStorageController> pController; 1567 rc = sMachine->AddStorageController(Bstr("Floppy Controller"), StorageBus_Floppy, pController.asOutParam()); 1568 if (FAILED(rc)) throw rc; 1569 1570 Bstr bstrName; 1571 rc = pController->COMGETTER(Name)(bstrName.asOutParam()); 1572 if (FAILED(rc)) throw rc; 1573 1574 // this is for rollback later 1575 MyHardDiskAttachment mhda; 1576 mhda.bstrUuid = bstrNewMachineId; 1577 mhda.pMachine = pNewMachine; 1578 mhda.controllerType = bstrName; 1579 mhda.lChannel = 0; 1580 mhda.lDevice = 0; 1581 1582 Log(("Attaching floppy\n")); 1583 1584 rc = sMachine->AttachDevice(mhda.controllerType, 1585 mhda.lChannel, 1586 mhda.lDevice, 1587 DeviceType_Floppy, 1588 NULL); 1589 if (FAILED(rc)) throw rc; 1590 1591 stack.llHardDiskAttachments.push_back(mhda); 1592 } 1593 1594 1595 // CD-ROMs next 1596 for (std::list<VirtualSystemDescriptionEntry*>::const_iterator jt = vsdeCDROM.begin(); 1597 jt != vsdeCDROM.end(); 1598 ++jt) 1599 { 1600 // for now always attach to secondary master on IDE controller; 1601 // there seems to be no useful information in OVF where else to 1602 // attach jt (@todo test with latest versions of OVF software) 1603 1604 // find the IDE controller 1605 const ovf::HardDiskController *pController = NULL; 1606 for (ovf::ControllersMap::const_iterator kt = vsysThis.mapControllers.begin(); 1607 kt != vsysThis.mapControllers.end(); 1608 ++kt) 1609 { 1610 if (kt->second.system == ovf::HardDiskController::IDE) 1611 { 1612 pController = &kt->second; 1613 } 1614 } 1615 1616 if (!pController) 1617 throw setError(VBOX_E_FILE_ERROR, 1618 tr("OVF wants a CD-ROM drive but cannot find IDE controller, which is required in this version of VirtualBox")); 1619 1620 // this is for rollback later 1621 MyHardDiskAttachment mhda; 1622 mhda.bstrUuid = bstrNewMachineId; 1623 mhda.pMachine = pNewMachine; 1624 1625 convertDiskAttachmentValues(*pController, 1626 2, // interpreted as secondary master 1627 mhda.controllerType, // Bstr 1628 mhda.lChannel, 1629 mhda.lDevice); 1630 1631 Log(("Attaching CD-ROM to channel %d on device %d\n", mhda.lChannel, mhda.lDevice)); 1632 1633 rc = sMachine->AttachDevice(mhda.controllerType, 1634 mhda.lChannel, 1635 mhda.lDevice, 1636 DeviceType_DVD, 1637 NULL); 1638 if (FAILED(rc)) throw rc; 1639 1640 stack.llHardDiskAttachments.push_back(mhda); 1641 } // end for (itHD = avsdeHDs.begin(); 1642 1643 rc = sMachine->SaveSettings(); 1644 if (FAILED(rc)) throw rc; 1645 1646 // only now that we're done with all disks, close the session 1647 rc = stack.pSession->Close(); 1648 if (FAILED(rc)) throw rc; 1649 stack.fSessionOpen = false; 1650 } 1651 catch(HRESULT /* aRC */) 1652 { 1653 if (stack.fSessionOpen) 1654 stack.pSession->Close(); 1655 1656 throw; 1657 } 1658 } 1659 1660 /* Create the hard disks & connect them to the appropriate controllers. */ 1661 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage); 1662 if (avsdeHDs.size() > 0) 1663 { 1664 // If there's an error here we need to close the session, so 1665 // we need another try/catch block. 1666 ComPtr<IMedium> srcHdVBox; 1667 bool fSourceHdNeedsClosing = false; 1668 1669 try 1670 { 1671 /* In order to attach hard disks we need to open a session 1672 * for the new machine */ 1673 rc = mVirtualBox->OpenSession(stack.pSession, bstrNewMachineId); 1674 if (FAILED(rc)) throw rc; 1675 stack.fSessionOpen = true; 1676 1677 /* The disk image has to be on the same place as the OVF file. So 1678 * strip the filename out of the full file path. */ 1679 Utf8Str strSrcDir(stack.locInfo.strPath); 1680 strSrcDir.stripFilename(); 1681 1682 /* Iterate over all given disk images */ 1683 list<VirtualSystemDescriptionEntry*>::const_iterator itHD; 1684 for (itHD = avsdeHDs.begin(); 1685 itHD != avsdeHDs.end(); 1686 ++itHD) 1687 { 1688 VirtualSystemDescriptionEntry *vsdeHD = *itHD; 1689 1690 /* Check if the destination file exists already or the 1691 * destination path is empty. */ 1692 if ( vsdeHD->strVbox.isEmpty() 1693 || RTPathExists(vsdeHD->strVbox.c_str()) 1694 ) 1695 /* This isn't allowed */ 1696 throw setError(VBOX_E_FILE_ERROR, 1697 tr("Destination file '%s' exists", 1698 vsdeHD->strVbox.c_str())); 1699 1700 /* Find the disk from the OVF's disk list */ 1701 ovf::DiskImagesMap::const_iterator itDiskImage = stack.mapDisks.find(vsdeHD->strRef); 1702 /* vsdeHD->strRef contains the disk identifier (e.g. "vmdisk1"), which should exist 1703 in the virtual system's disks map under that ID and also in the global images map. */ 1704 ovf::VirtualDisksMap::const_iterator itVirtualDisk = vsysThis.mapVirtualDisks.find(vsdeHD->strRef); 1705 1706 if ( itDiskImage == stack.mapDisks.end() 1707 || itVirtualDisk == vsysThis.mapVirtualDisks.end() 1708 ) 1709 throw setError(E_FAIL, 1710 tr("Internal inconsistency looking up disk images.")); 1711 1712 const ovf::DiskImage &di = itDiskImage->second; 1713 const ovf::VirtualDisk &vd = itVirtualDisk->second; 1714 1715 /* Make sure all target directories exists */ 1716 rc = VirtualBox::ensureFilePathExists(vsdeHD->strVbox.c_str()); 1717 if (FAILED(rc)) 1718 throw rc; 1719 1720 // subprogress object for hard disk 1721 ComPtr<IProgress> pProgress2; 1722 1723 ComPtr<IMedium> dstHdVBox; 1724 /* If strHref is empty we have to create a new file */ 1725 if (di.strHref.isEmpty()) 1726 { 1727 /* Which format to use? */ 1728 Bstr srcFormat = L"VDI"; 1729 if ( di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#sparse", Utf8Str::CaseInsensitive) 1730 || di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive)) 1731 srcFormat = L"VMDK"; 1732 /* Create an empty hard disk */ 1733 rc = mVirtualBox->CreateHardDisk(srcFormat, Bstr(vsdeHD->strVbox), dstHdVBox.asOutParam()); 1734 if (FAILED(rc)) throw rc; 1735 1736 /* Create a dynamic growing disk image with the given capacity */ 1737 rc = dstHdVBox->CreateBaseStorage(di.iCapacity / _1M, MediumVariant_Standard, pProgress2.asOutParam()); 1738 if (FAILED(rc)) throw rc; 1739 1740 /* Advance to the next operation */ 1741 stack.pProgress->SetNextOperation(BstrFmt(tr("Creating virtual disk image '%s'"), vsdeHD->strVbox.c_str()), 1742 vsdeHD->ulSizeMB); // operation's weight, as set up with the IProgress originally 1743 } 1744 else 1745 { 1746 /* Construct the source file path */ 1747 Utf8StrFmt strSrcFilePath("%s%c%s", strSrcDir.c_str(), RTPATH_DELIMITER, di.strHref.c_str()); 1748 /* Check if the source file exists */ 1749 if (!RTPathExists(strSrcFilePath.c_str())) 1750 /* This isn't allowed */ 1751 throw setError(VBOX_E_FILE_ERROR, 1752 tr("Source virtual disk image file '%s' doesn't exist"), 1753 strSrcFilePath.c_str()); 1754 1755 /* Clone the disk image (this is necessary cause the id has 1756 * to be recreated for the case the same hard disk is 1757 * attached already from a previous import) */ 1758 1759 /* First open the existing disk image */ 1760 rc = mVirtualBox->OpenHardDisk(Bstr(strSrcFilePath), 1761 AccessMode_ReadOnly, 1762 false, 1763 NULL, 1764 false, 1765 NULL, 1766 srcHdVBox.asOutParam()); 1767 if (FAILED(rc)) throw rc; 1768 fSourceHdNeedsClosing = true; 1769 1770 /* We need the format description of the source disk image */ 1771 Bstr srcFormat; 1772 rc = srcHdVBox->COMGETTER(Format)(srcFormat.asOutParam()); 1773 if (FAILED(rc)) throw rc; 1774 /* Create a new hard disk interface for the destination disk image */ 1775 rc = mVirtualBox->CreateHardDisk(srcFormat, Bstr(vsdeHD->strVbox), dstHdVBox.asOutParam()); 1776 if (FAILED(rc)) throw rc; 1777 /* Clone the source disk image */ 1778 rc = srcHdVBox->CloneTo(dstHdVBox, MediumVariant_Standard, NULL, pProgress2.asOutParam()); 1779 if (FAILED(rc)) throw rc; 1780 1781 /* Advance to the next operation */ 1782 stack.pProgress->SetNextOperation(BstrFmt(tr("Importing virtual disk image '%s'"), strSrcFilePath.c_str()), 1783 vsdeHD->ulSizeMB); // operation's weight, as set up with the IProgress originally); 1784 } 1785 1786 // now wait for the background disk operation to complete; this throws HRESULTs on error 1787 waitForAsyncProgress(stack.pProgress, pProgress2); 1788 1789 if (fSourceHdNeedsClosing) 1790 { 1791 rc = srcHdVBox->Close(); 1792 if (FAILED(rc)) throw rc; 1793 fSourceHdNeedsClosing = false; 1794 } 1795 1796 stack.llHardDisksCreated.push_back(dstHdVBox); 1797 /* Now use the new uuid to attach the disk image to our new machine */ 1798 ComPtr<IMachine> sMachine; 1799 rc = stack.pSession->COMGETTER(Machine)(sMachine.asOutParam()); 1800 if (FAILED(rc)) throw rc; 1801 Bstr hdId; 1802 rc = dstHdVBox->COMGETTER(Id)(hdId.asOutParam()); 1803 if (FAILED(rc)) throw rc; 1804 1805 /* For now we assume we have one controller of every type only */ 1806 ovf::HardDiskController hdc = (*vsysThis.mapControllers.find(vd.idController)).second; 1807 1808 // this is for rollback later 1809 MyHardDiskAttachment mhda; 1810 mhda.bstrUuid = bstrNewMachineId; 1811 mhda.pMachine = pNewMachine; 1812 1813 convertDiskAttachmentValues(hdc, 1814 vd.ulAddressOnParent, 1815 mhda.controllerType, // Bstr 1816 mhda.lChannel, 1817 mhda.lDevice); 1818 1819 Log(("Attaching disk %s to channel %d on device %d\n", vsdeHD->strVbox.c_str(), mhda.lChannel, mhda.lDevice)); 1820 1821 rc = sMachine->AttachDevice(mhda.controllerType, 1822 mhda.lChannel, 1823 mhda.lDevice, 1824 DeviceType_HardDisk, 1825 hdId); 1826 if (FAILED(rc)) throw rc; 1827 1828 stack.llHardDiskAttachments.push_back(mhda); 1829 1830 rc = sMachine->SaveSettings(); 1831 if (FAILED(rc)) throw rc; 1832 } // end for (itHD = avsdeHDs.begin(); 1833 1834 // only now that we're done with all disks, close the session 1835 rc = stack.pSession->Close(); 1836 if (FAILED(rc)) throw rc; 1837 stack.fSessionOpen = false; 1838 } 1839 catch(HRESULT /* aRC */) 1840 { 1841 if (fSourceHdNeedsClosing) 1842 srcHdVBox->Close(); 1843 1844 if (stack.fSessionOpen) 1845 stack.pSession->Close(); 1846 1847 throw; 1848 } 1849 } 1850 } 1851 1852 /** 1853 * Imports one OVF virtual system (described by a vbox:Machine tag represented by the given config 1854 * structure) into VirtualBox by creating an IMachine instance, which is returned. 1855 * 1856 * This throws HRESULT error codes for anything that goes wrong, in which case the caller must clean 1857 * up any leftovers from this function. For this, the given ImportStack instance has received information 1858 * about what needs cleaning up (to support rollback). 1859 * 1860 * @param config 1861 * @param pNewMachine 1862 * @param stack 1863 */ 1864 void Appliance::importVBoxMachine(const settings::MachineConfigFile &config, 1865 ComPtr<IMachine> &pNewMachine, 1866 ImportStack &stack) 1867 { 1791 1868 } 1792 1869 -
trunk/src/VBox/Main/MachineImpl.cpp
r28091 r28098 7500 7500 * from the previous machine config file in the instance data, if any. 7501 7501 * 7502 * This fills all the fields in there, including snapshots, *except* 7502 * This gets called from two locations: 7503 * 7504 * -- Machine::saveSettings(), during the regular XML writing; 7505 * 7506 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets 7507 * exported to OVF and we write the VirtualBox proprietary XML 7508 * into a <vbox:Machine> tag. 7509 * 7510 * This routine fills all the fields in there, including snapshots, *except* 7503 7511 * for the following: 7504 7512 * -
trunk/src/VBox/Main/include/ApplianceImpl.h
r27918 r28098 35 35 { 36 36 struct HardDiskController; 37 struct VirtualSystem; 37 38 class OVFReader; 38 39 } … … 146 147 147 148 HRESULT importFS(const LocationInfo &locInfo, ComObjPtr<Progress> &aProgress); 149 struct ImportStack; 150 void importVBoxMachine(const settings::MachineConfigFile &config, 151 ComPtr<IMachine> &pNewMachine, 152 ImportStack &stack); 153 void importMachineGeneric(const ovf::VirtualSystem &vsysThis, 154 ComObjPtr<VirtualSystemDescription> &vsdescThis, 155 ComPtr<IMachine> &pNewMachine, 156 ImportStack &stack); 148 157 HRESULT importS3(TaskOVF *pTask); 149 158 -
trunk/src/VBox/Main/xml/Settings.cpp
r27918 r28098 3693 3693 /** 3694 3694 * Builds the XML DOM tree for the machine config under the given XML element. 3695 * 3695 3696 * This has been separated out from write() so it can be called from elsewhere, 3696 3697 * such as the OVF code, to build machine XML in an existing XML tree. 3697 * @param elmMachine 3698 */ 3699 void MachineConfigFile::buildMachineXML(xml::ElementNode &elmMachine) 3698 * 3699 * As a result, this gets called from two locations: 3700 * 3701 * -- MachineConfigFile::write(); 3702 * 3703 * -- Appliance::buildXMLForOneVirtualSystem() 3704 * 3705 * @param elmMachine XML <Machine> element to add attributes and elements to. 3706 * @param fWriteSnapshots If false, we omit snapshots entirely (we don't recurse then). 3707 */ 3708 void MachineConfigFile::buildMachineXML(xml::ElementNode &elmMachine, 3709 bool fIncludeSnapshots) 3700 3710 { 3701 3711 elmMachine.setAttribute("uuid", makeString(uuid)); … … 3708 3718 if (strStateFile.length()) 3709 3719 elmMachine.setAttribute("stateFile", strStateFile); 3710 if ( !uuidCurrentSnapshot.isEmpty())3720 if (fIncludeSnapshots && !uuidCurrentSnapshot.isEmpty()) 3711 3721 elmMachine.setAttribute("currentSnapshot", makeString(uuidCurrentSnapshot)); 3712 3722 if (strSnapshotFolder.length()) … … 3734 3744 writeExtraData(elmMachine, mapExtraDataItems); 3735 3745 3736 if ( llFirstSnapshot.size())3746 if (fIncludeSnapshots && llFirstSnapshot.size()) 3737 3747 buildSnapshotXML(elmMachine, llFirstSnapshot.front()); 3738 3748 … … 3901 3911 3902 3912 xml::ElementNode *pelmMachine = m->pelmRoot->createChild("Machine"); 3903 buildMachineXML(*pelmMachine); 3913 buildMachineXML(*pelmMachine, 3914 true /* fIncludeSnapshots */); 3904 3915 3905 3916 // now go write the XML
Note:
See TracChangeset
for help on using the changeset viewer.