VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxManage/VBoxManageAppliance.cpp@ 77910

Last change on this file since 77910 was 76553, checked in by vboxsync, 5 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 73.8 KB
RevLine 
[16491]1/* $Id: VBoxManageAppliance.cpp 76553 2019-01-01 01:45:53Z vboxsync $ */
2/** @file
3 * VBoxManage - The appliance-related commands.
4 */
5
6/*
[76553]7 * Copyright (C) 2009-2019 Oracle Corporation
[16491]8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#ifndef VBOX_ONLY_DOCS
19
[57358]20
21/*********************************************************************************************************************************
22* Header Files *
23*********************************************************************************************************************************/
[16495]24#ifndef VBOX_ONLY_DOCS
25#include <VBox/com/com.h>
26#include <VBox/com/string.h>
27#include <VBox/com/Guid.h>
28#include <VBox/com/array.h>
29#include <VBox/com/ErrorInfo.h>
[20928]30#include <VBox/com/errorprint.h>
[16495]31#include <VBox/com/VirtualBox.h>
32
33#include <list>
[16601]34#include <map>
[16495]35#endif /* !VBOX_ONLY_DOCS */
36
37#include <iprt/stream.h>
[17098]38#include <iprt/getopt.h>
[18108]39#include <iprt/ctype.h>
[18775]40#include <iprt/path.h>
[18809]41#include <iprt/file.h>
[16495]42
43#include <VBox/log.h>
[25165]44#include <VBox/param.h>
[16495]45
[16491]46#include "VBoxManage.h"
47using namespace com;
48
49
50// funcs
51///////////////////////////////////////////////////////////////////////////////
52
[18754]53typedef std::map<Utf8Str, Utf8Str> ArgsMap; // pairs of strings like "vmname" => "newvmname"
[16606]54typedef std::map<uint32_t, ArgsMap> ArgsMapsMap; // map of maps, one for each virtual system, sorted by index
[16601]55
[16606]56typedef std::map<uint32_t, bool> IgnoresMap; // pairs of numeric description entry indices
57typedef std::map<uint32_t, IgnoresMap> IgnoresMapsMap; // map of maps, one for each virtual system, sorted by index
58
[16601]59static bool findArgValue(Utf8Str &strOut,
[16606]60 ArgsMap *pmapArgs,
[16601]61 const Utf8Str &strKey)
62{
63 if (pmapArgs)
64 {
[16606]65 ArgsMap::iterator it;
[16601]66 it = pmapArgs->find(strKey);
67 if (it != pmapArgs->end())
68 {
69 strOut = it->second;
[16606]70 pmapArgs->erase(it);
[16601]71 return true;
72 }
73 }
74
75 return false;
76}
77
[37862]78static int parseImportOptions(const char *psz, com::SafeArray<ImportOptions_T> *options)
79{
80 int rc = VINF_SUCCESS;
81 while (psz && *psz && RT_SUCCESS(rc))
82 {
83 size_t len;
84 const char *pszComma = strchr(psz, ',');
85 if (pszComma)
86 len = pszComma - psz;
87 else
88 len = strlen(psz);
89 if (len > 0)
90 {
91 if (!RTStrNICmp(psz, "KeepAllMACs", len))
92 options->push_back(ImportOptions_KeepAllMACs);
93 else if (!RTStrNICmp(psz, "KeepNATMACs", len))
94 options->push_back(ImportOptions_KeepNATMACs);
[54979]95 else if (!RTStrNICmp(psz, "ImportToVDI", len))
96 options->push_back(ImportOptions_ImportToVDI);
[37862]97 else
98 rc = VERR_PARSE_ERROR;
99 }
100 if (pszComma)
101 psz += len + 1;
102 else
103 psz += len;
104 }
105
106 return rc;
107}
108
[18754]109static const RTGETOPTDEF g_aImportApplianceOptions[] =
110{
111 { "--dry-run", 'n', RTGETOPT_REQ_NOTHING },
112 { "-dry-run", 'n', RTGETOPT_REQ_NOTHING }, // deprecated
113 { "--dryrun", 'n', RTGETOPT_REQ_NOTHING },
114 { "-dryrun", 'n', RTGETOPT_REQ_NOTHING }, // deprecated
115 { "--detailed-progress", 'P', RTGETOPT_REQ_NOTHING },
116 { "-detailed-progress", 'P', RTGETOPT_REQ_NOTHING }, // deprecated
117 { "--vsys", 's', RTGETOPT_REQ_UINT32 },
118 { "-vsys", 's', RTGETOPT_REQ_UINT32 }, // deprecated
119 { "--ostype", 'o', RTGETOPT_REQ_STRING },
120 { "-ostype", 'o', RTGETOPT_REQ_STRING }, // deprecated
121 { "--vmname", 'V', RTGETOPT_REQ_STRING },
122 { "-vmname", 'V', RTGETOPT_REQ_STRING }, // deprecated
[72476]123 { "--settingsfile", 'S', RTGETOPT_REQ_STRING },
124 { "--basefolder", 'p', RTGETOPT_REQ_STRING },
125 { "--group", 'g', RTGETOPT_REQ_STRING },
[25142]126 { "--memory", 'm', RTGETOPT_REQ_STRING },
127 { "-memory", 'm', RTGETOPT_REQ_STRING }, // deprecated
128 { "--cpus", 'c', RTGETOPT_REQ_STRING },
[18809]129 { "--description", 'd', RTGETOPT_REQ_STRING },
[18754]130 { "--eula", 'L', RTGETOPT_REQ_STRING },
131 { "-eula", 'L', RTGETOPT_REQ_STRING }, // deprecated
132 { "--unit", 'u', RTGETOPT_REQ_UINT32 },
133 { "-unit", 'u', RTGETOPT_REQ_UINT32 }, // deprecated
134 { "--ignore", 'x', RTGETOPT_REQ_NOTHING },
135 { "-ignore", 'x', RTGETOPT_REQ_NOTHING }, // deprecated
136 { "--scsitype", 'T', RTGETOPT_REQ_UINT32 },
137 { "-scsitype", 'T', RTGETOPT_REQ_UINT32 }, // deprecated
138 { "--type", 'T', RTGETOPT_REQ_UINT32 }, // deprecated
139 { "-type", 'T', RTGETOPT_REQ_UINT32 }, // deprecated
[33363]140#if 0 /* Changing the controller is fully valid, but the current design on how
141 the params are evaluated here doesn't allow two parameter for one
[33372]142 unit. The target disk path is more important. I leave it for future
[33363]143 improvments. */
144 { "--controller", 'C', RTGETOPT_REQ_STRING },
145#endif
[33361]146 { "--disk", 'D', RTGETOPT_REQ_STRING },
[37862]147 { "--options", 'O', RTGETOPT_REQ_STRING },
[18754]148};
149
[56118]150RTEXITCODE handleImportAppliance(HandlerArg *arg)
[16491]151{
152 HRESULT rc = S_OK;
153
[16495]154 Utf8Str strOvfFilename;
[16830]155 bool fExecute = true; // if true, then we actually do the import
[37862]156 com::SafeArray<ImportOptions_T> options;
[16601]157 uint32_t ulCurVsys = (uint32_t)-1;
[18754]158 uint32_t ulCurUnit = (uint32_t)-1;
159 // for each --vsys X command, maintain a map of command line items
[16601]160 // (we'll parse them later after interpreting the OVF, when we can
161 // actually check whether they make sense semantically)
162 ArgsMapsMap mapArgsMapsPerVsys;
[16606]163 IgnoresMapsMap mapIgnoresMapsPerVsys;
[16601]164
[18754]165 int c;
166 RTGETOPTUNION ValueUnion;
167 RTGETOPTSTATE GetState;
168 // start at 0 because main() has hacked both the argc and argv given to us
[26517]169 RTGetOptInit(&GetState, arg->argc, arg->argv, g_aImportApplianceOptions, RT_ELEMENTS(g_aImportApplianceOptions),
170 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
[18754]171 while ((c = RTGetOpt(&GetState, &ValueUnion)))
[16495]172 {
[18754]173 switch (c)
[16601]174 {
[18754]175 case 'n': // --dry-run
176 fExecute = false;
177 break;
[16606]178
[18754]179 case 'P': // --detailed-progress
180 g_fDetailedProgress = true;
181 break;
[16601]182
[18754]183 case 's': // --vsys
184 ulCurVsys = ValueUnion.u32;
185 ulCurUnit = (uint32_t)-1;
186 break;
[16606]187
[18754]188 case 'o': // --ostype
189 if (ulCurVsys == (uint32_t)-1)
190 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
191 mapArgsMapsPerVsys[ulCurVsys]["ostype"] = ValueUnion.psz;
192 break;
193
194 case 'V': // --vmname
195 if (ulCurVsys == (uint32_t)-1)
196 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
197 mapArgsMapsPerVsys[ulCurVsys]["vmname"] = ValueUnion.psz;
198 break;
199
[72476]200 case 'S': // --settingsfile
201 if (ulCurVsys == (uint32_t)-1)
202 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
203 mapArgsMapsPerVsys[ulCurVsys]["settingsfile"] = ValueUnion.psz;
204 break;
205
206 case 'p': // --basefolder
207 if (ulCurVsys == (uint32_t)-1)
208 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
209 mapArgsMapsPerVsys[ulCurVsys]["basefolder"] = ValueUnion.psz;
210 break;
211
212 case 'g': // --group
213 if (ulCurVsys == (uint32_t)-1)
214 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
215 mapArgsMapsPerVsys[ulCurVsys]["group"] = ValueUnion.psz;
216 break;
217
[18809]218 case 'd': // --description
219 if (ulCurVsys == (uint32_t)-1)
220 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
221 mapArgsMapsPerVsys[ulCurVsys]["description"] = ValueUnion.psz;
222 break;
223
224 case 'L': // --eula
225 if (ulCurVsys == (uint32_t)-1)
226 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
227 mapArgsMapsPerVsys[ulCurVsys]["eula"] = ValueUnion.psz;
228 break;
229
[18754]230 case 'm': // --memory
231 if (ulCurVsys == (uint32_t)-1)
232 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
233 mapArgsMapsPerVsys[ulCurVsys]["memory"] = ValueUnion.psz;
234 break;
235
[25142]236 case 'c': // --cpus
237 if (ulCurVsys == (uint32_t)-1)
238 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
239 mapArgsMapsPerVsys[ulCurVsys]["cpus"] = ValueUnion.psz;
240 break;
241
[18754]242 case 'u': // --unit
243 ulCurUnit = ValueUnion.u32;
244 break;
245
246 case 'x': // --ignore
247 if (ulCurVsys == (uint32_t)-1)
248 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
249 if (ulCurUnit == (uint32_t)-1)
250 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --unit argument.", GetState.pDef->pszLong);
251 mapIgnoresMapsPerVsys[ulCurVsys][ulCurUnit] = true;
252 break;
253
254 case 'T': // --scsitype
255 if (ulCurVsys == (uint32_t)-1)
256 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
257 if (ulCurUnit == (uint32_t)-1)
258 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --unit argument.", GetState.pDef->pszLong);
259 mapArgsMapsPerVsys[ulCurVsys][Utf8StrFmt("scsitype%u", ulCurUnit)] = ValueUnion.psz;
260 break;
261
262 case 'C': // --controller
263 if (ulCurVsys == (uint32_t)-1)
264 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
265 if (ulCurUnit == (uint32_t)-1)
266 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --unit argument.", GetState.pDef->pszLong);
267 mapArgsMapsPerVsys[ulCurVsys][Utf8StrFmt("controller%u", ulCurUnit)] = ValueUnion.psz;
268 break;
269
[33361]270 case 'D': // --disk
271 if (ulCurVsys == (uint32_t)-1)
272 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
273 if (ulCurUnit == (uint32_t)-1)
274 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --unit argument.", GetState.pDef->pszLong);
275 mapArgsMapsPerVsys[ulCurVsys][Utf8StrFmt("disk%u", ulCurUnit)] = ValueUnion.psz;
276 break;
277
[37862]278 case 'O': // --options
279 if (RT_FAILURE(parseImportOptions(ValueUnion.psz, &options)))
280 return errorArgument("Invalid import options '%s'\n", ValueUnion.psz);
281 break;
282
[18754]283 case VINF_GETOPT_NOT_OPTION:
[21394]284 if (strOvfFilename.isEmpty())
[18754]285 strOvfFilename = ValueUnion.psz;
[16606]286 else
[18754]287 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Invalid parameter '%s'", ValueUnion.psz);
288 break;
289
290 default:
291 if (c > 0)
[16606]292 {
[18754]293 if (RT_C_IS_PRINT(c))
294 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Invalid option -%c", c);
295 else
296 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Invalid option case %i", c);
[16606]297 }
[18754]298 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
299 return errorSyntax(USAGE_IMPORTAPPLIANCE, "unknown option: %s\n", ValueUnion.psz);
300 else if (ValueUnion.pDef)
301 return errorSyntax(USAGE_IMPORTAPPLIANCE, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
302 else
303 return errorSyntax(USAGE_IMPORTAPPLIANCE, "error: %Rrs", c);
[16601]304 }
[16495]305 }
306
[21394]307 if (strOvfFilename.isEmpty())
[16495]308 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Not enough arguments for \"import\" command.");
309
310 do
311 {
[17033]312 ComPtr<IAppliance> pAppliance;
[24998]313 CHECK_ERROR_BREAK(arg->virtualBox, CreateAppliance(pAppliance.asOutParam()));
[16495]314
[21612]315 char *pszAbsFilePath;
[36527]316 if (strOvfFilename.startsWith("S3://", RTCString::CaseInsensitive) ||
317 strOvfFilename.startsWith("SunCloud://", RTCString::CaseInsensitive) ||
318 strOvfFilename.startsWith("webdav://", RTCString::CaseInsensitive))
[21612]319 pszAbsFilePath = RTStrDup(strOvfFilename.c_str());
320 else
321 pszAbsFilePath = RTPathAbsDup(strOvfFilename.c_str());
322 ComPtr<IProgress> progressRead;
[32718]323 CHECK_ERROR_BREAK(pAppliance, Read(Bstr(pszAbsFilePath).raw(),
324 progressRead.asOutParam()));
[18775]325 RTStrFree(pszAbsFilePath);
[16662]326
[21612]327 rc = showProgress(progressRead);
[38525]328 CHECK_PROGRESS_ERROR_RET(progressRead, ("Appliance read failed"), RTEXITCODE_FAILURE);
[21612]329
330 Bstr path; /* fetch the path, there is stuff like username/password removed if any */
331 CHECK_ERROR_BREAK(pAppliance, COMGETTER(Path)(path.asOutParam()));
[17827]332 // call interpret(); this can yield both warnings and errors, so we need
333 // to tinker with the error info a bit
[32701]334 RTStrmPrintf(g_pStdErr, "Interpreting %ls...\n", path.raw());
[17827]335 rc = pAppliance->Interpret();
[30681]336 com::ErrorInfo info0(pAppliance, COM_IIDOF(IAppliance));
[17827]337
338 com::SafeArray<BSTR> aWarnings;
339 if (SUCCEEDED(pAppliance->GetWarnings(ComSafeArrayAsOutParam(aWarnings))))
340 {
[18489]341 size_t cWarnings = aWarnings.size();
[17827]342 for (unsigned i = 0; i < cWarnings; ++i)
343 {
344 Bstr bstrWarning(aWarnings[i]);
[32701]345 RTMsgWarning("%ls.", bstrWarning.raw());
[17827]346 }
347 }
348
349 if (FAILED(rc)) // during interpret, after printing warnings
350 {
351 com::GluePrintErrorInfo(info0);
352 com::GluePrintErrorContext("Interpret", __FILE__, __LINE__);
353 break;
354 }
355
[32701]356 RTStrmPrintf(g_pStdErr, "OK.\n");
[16495]357
358 // fetch all disks
359 com::SafeArray<BSTR> retDisks;
[17033]360 CHECK_ERROR_BREAK(pAppliance,
[16495]361 COMGETTER(Disks)(ComSafeArrayAsOutParam(retDisks)));
362 if (retDisks.size() > 0)
363 {
[54979]364 RTPrintf("Disks:\n");
[16495]365 for (unsigned i = 0; i < retDisks.size(); i++)
[54979]366 RTPrintf(" %ls\n", retDisks[i]);
[16495]367 RTPrintf("\n");
368 }
369
370 // fetch virtual system descriptions
371 com::SafeIfaceArray<IVirtualSystemDescription> aVirtualSystemDescriptions;
[17033]372 CHECK_ERROR_BREAK(pAppliance,
[16495]373 COMGETTER(VirtualSystemDescriptions)(ComSafeArrayAsOutParam(aVirtualSystemDescriptions)));
[16601]374
[18489]375 size_t cVirtualSystemDescriptions = aVirtualSystemDescriptions.size();
[16601]376
377 // match command line arguments with virtual system descriptions;
378 // this is only to sort out invalid indices at this time
379 ArgsMapsMap::const_iterator it;
380 for (it = mapArgsMapsPerVsys.begin();
381 it != mapArgsMapsPerVsys.end();
382 ++it)
[16495]383 {
[16601]384 uint32_t ulVsys = it->first;
385 if (ulVsys >= cVirtualSystemDescriptions)
386 return errorSyntax(USAGE_IMPORTAPPLIANCE,
[18490]387 "Invalid index %RI32 with -vsys option; the OVF contains only %zu virtual system(s).",
[16601]388 ulVsys, cVirtualSystemDescriptions);
389 }
390
[18223]391 uint32_t cLicensesInTheWay = 0;
392
[16601]393 // dump virtual system descriptions and match command-line arguments
394 if (cVirtualSystemDescriptions > 0)
395 {
396 for (unsigned i = 0; i < cVirtualSystemDescriptions; ++i)
[16495]397 {
398 com::SafeArray<VirtualSystemDescriptionType_T> retTypes;
[16515]399 com::SafeArray<BSTR> aRefs;
[17291]400 com::SafeArray<BSTR> aOvfValues;
[50323]401 com::SafeArray<BSTR> aVBoxValues;
[16495]402 com::SafeArray<BSTR> aExtraConfigValues;
403 CHECK_ERROR_BREAK(aVirtualSystemDescriptions[i],
404 GetDescription(ComSafeArrayAsOutParam(retTypes),
405 ComSafeArrayAsOutParam(aRefs),
[17291]406 ComSafeArrayAsOutParam(aOvfValues),
[50323]407 ComSafeArrayAsOutParam(aVBoxValues),
[16495]408 ComSafeArrayAsOutParam(aExtraConfigValues)));
409
[18754]410 RTPrintf("Virtual system %u:\n", i);
[16601]411
412 // look up the corresponding command line options, if any
[16606]413 ArgsMap *pmapArgs = NULL;
414 ArgsMapsMap::iterator itm = mapArgsMapsPerVsys.find(i);
[16601]415 if (itm != mapArgsMapsPerVsys.end())
416 pmapArgs = &itm->second;
417
418 // this collects the final values for setFinalValues()
419 com::SafeArray<BOOL> aEnabled(retTypes.size());
420 com::SafeArray<BSTR> aFinalValues(retTypes.size());
421
[16495]422 for (unsigned a = 0; a < retTypes.size(); ++a)
423 {
424 VirtualSystemDescriptionType_T t = retTypes[a];
425
[16601]426 Utf8Str strOverride;
[16495]427
[50323]428 Bstr bstrFinalValue = aVBoxValues[a];
[16601]429
[16606]430 bool fIgnoreThis = mapIgnoresMapsPerVsys[i][a];
431
[16830]432 aEnabled[a] = true;
433
[16495]434 switch (t)
435 {
[18809]436 case VirtualSystemDescriptionType_OS:
437 if (findArgValue(strOverride, pmapArgs, "ostype"))
438 {
439 bstrFinalValue = strOverride;
440 RTPrintf("%2u: OS type specified with --ostype: \"%ls\"\n",
441 a, bstrFinalValue.raw());
442 }
443 else
444 RTPrintf("%2u: Suggested OS type: \"%ls\""
445 "\n (change with \"--vsys %u --ostype <type>\"; use \"list ostypes\" to list all possible values)\n",
446 a, bstrFinalValue.raw(), i);
[63300]447 break;
[18809]448
[16495]449 case VirtualSystemDescriptionType_Name:
[18754]450 if (findArgValue(strOverride, pmapArgs, "vmname"))
[16601]451 {
452 bstrFinalValue = strOverride;
[18754]453 RTPrintf("%2u: VM name specified with --vmname: \"%ls\"\n",
[16601]454 a, bstrFinalValue.raw());
455 }
456 else
[18754]457 RTPrintf("%2u: Suggested VM name \"%ls\""
458 "\n (change with \"--vsys %u --vmname <name>\")\n",
[16601]459 a, bstrFinalValue.raw(), i);
[63300]460 break;
[16495]461
[18809]462 case VirtualSystemDescriptionType_Product:
463 RTPrintf("%2u: Product (ignored): %ls\n",
[50323]464 a, aVBoxValues[a]);
[63300]465 break;
[18809]466
467 case VirtualSystemDescriptionType_ProductUrl:
468 RTPrintf("%2u: ProductUrl (ignored): %ls\n",
[50323]469 a, aVBoxValues[a]);
[63300]470 break;
[18809]471
472 case VirtualSystemDescriptionType_Vendor:
473 RTPrintf("%2u: Vendor (ignored): %ls\n",
[50323]474 a, aVBoxValues[a]);
[63300]475 break;
[18809]476
477 case VirtualSystemDescriptionType_VendorUrl:
478 RTPrintf("%2u: VendorUrl (ignored): %ls\n",
[50323]479 a, aVBoxValues[a]);
[63300]480 break;
[18809]481
482 case VirtualSystemDescriptionType_Version:
483 RTPrintf("%2u: Version (ignored): %ls\n",
[50323]484 a, aVBoxValues[a]);
[63300]485 break;
[18809]486
487 case VirtualSystemDescriptionType_Description:
488 if (findArgValue(strOverride, pmapArgs, "description"))
[16601]489 {
490 bstrFinalValue = strOverride;
[18809]491 RTPrintf("%2u: Description specified with --description: \"%ls\"\n",
[16601]492 a, bstrFinalValue.raw());
493 }
494 else
[18809]495 RTPrintf("%2u: Description \"%ls\""
496 "\n (change with \"--vsys %u --description <desc>\")\n",
[16601]497 a, bstrFinalValue.raw(), i);
[63300]498 break;
[16495]499
[18223]500 case VirtualSystemDescriptionType_License:
501 ++cLicensesInTheWay;
[18754]502 if (findArgValue(strOverride, pmapArgs, "eula"))
[18223]503 {
504 if (strOverride == "show")
505 {
[18754]506 RTPrintf("%2u: End-user license agreement"
507 "\n (accept with \"--vsys %u --eula accept\"):"
[18223]508 "\n\n%ls\n\n",
509 a, i, bstrFinalValue.raw());
510 }
511 else if (strOverride == "accept")
512 {
[18754]513 RTPrintf("%2u: End-user license agreement (accepted)\n",
[18223]514 a);
515 --cLicensesInTheWay;
516 }
517 else
518 return errorSyntax(USAGE_IMPORTAPPLIANCE,
[18754]519 "Argument to --eula must be either \"show\" or \"accept\".");
[18223]520 }
521 else
[18754]522 RTPrintf("%2u: End-user license agreement"
[18809]523 "\n (display with \"--vsys %u --eula show\";"
524 "\n accept with \"--vsys %u --eula accept\")\n",
[18223]525 a, i, i);
[63300]526 break;
[18223]527
[16495]528 case VirtualSystemDescriptionType_CPU:
[25142]529 if (findArgValue(strOverride, pmapArgs, "cpus"))
530 {
531 uint32_t cCPUs;
[25165]532 if ( strOverride.toInt(cCPUs) == VINF_SUCCESS
533 && cCPUs >= VMM_MIN_CPU_COUNT
534 && cCPUs <= VMM_MAX_CPU_COUNT
[25142]535 )
536 {
537 bstrFinalValue = strOverride;
538 RTPrintf("%2u: No. of CPUs specified with --cpus: %ls\n",
539 a, bstrFinalValue.raw());
540 }
541 else
542 return errorSyntax(USAGE_IMPORTAPPLIANCE,
[25165]543 "Argument to --cpus option must be a number greater than %d and less than %d.",
544 VMM_MIN_CPU_COUNT - 1, VMM_MAX_CPU_COUNT + 1);
[25142]545 }
546 else
547 RTPrintf("%2u: Number of CPUs: %ls\n (change with \"--vsys %u --cpus <n>\")\n",
548 a, bstrFinalValue.raw(), i);
[63300]549 break;
[16495]550
551 case VirtualSystemDescriptionType_Memory:
[16601]552 {
[18754]553 if (findArgValue(strOverride, pmapArgs, "memory"))
[16601]554 {
555 uint32_t ulMemMB;
556 if (VINF_SUCCESS == strOverride.toInt(ulMemMB))
557 {
558 bstrFinalValue = strOverride;
[28768]559 RTPrintf("%2u: Guest memory specified with --memory: %ls MB\n",
[16601]560 a, bstrFinalValue.raw());
561 }
562 else
563 return errorSyntax(USAGE_IMPORTAPPLIANCE,
[18754]564 "Argument to --memory option must be a non-negative number.");
[16601]565 }
566 else
[28768]567 RTPrintf("%2u: Guest memory: %ls MB\n (change with \"--vsys %u --memory <MB>\")\n",
[16601]568 a, bstrFinalValue.raw(), i);
[63300]569 break;
[16601]570 }
[16495]571
572 case VirtualSystemDescriptionType_HardDiskControllerIDE:
[16606]573 if (fIgnoreThis)
574 {
[18754]575 RTPrintf("%2u: IDE controller, type %ls -- disabled\n",
[16606]576 a,
[50323]577 aVBoxValues[a]);
[16606]578 aEnabled[a] = false;
579 }
580 else
[18754]581 RTPrintf("%2u: IDE controller, type %ls"
582 "\n (disable with \"--vsys %u --unit %u --ignore\")\n",
[16606]583 a,
[50323]584 aVBoxValues[a],
[16606]585 i, a);
[63300]586 break;
[16495]587
588 case VirtualSystemDescriptionType_HardDiskControllerSATA:
[16606]589 if (fIgnoreThis)
590 {
[18754]591 RTPrintf("%2u: SATA controller, type %ls -- disabled\n",
[16606]592 a,
[50323]593 aVBoxValues[a]);
[16606]594 aEnabled[a] = false;
595 }
596 else
[18754]597 RTPrintf("%2u: SATA controller, type %ls"
598 "\n (disable with \"--vsys %u --unit %u --ignore\")\n",
[16606]599 a,
[50323]600 aVBoxValues[a],
[16606]601 i, a);
[63300]602 break;
[16495]603
[29994]604 case VirtualSystemDescriptionType_HardDiskControllerSAS:
605 if (fIgnoreThis)
606 {
607 RTPrintf("%2u: SAS controller, type %ls -- disabled\n",
608 a,
[50323]609 aVBoxValues[a]);
[29994]610 aEnabled[a] = false;
611 }
612 else
613 RTPrintf("%2u: SAS controller, type %ls"
614 "\n (disable with \"--vsys %u --unit %u --ignore\")\n",
615 a,
[50323]616 aVBoxValues[a],
[29994]617 i, a);
[63300]618 break;
[29994]619
[16495]620 case VirtualSystemDescriptionType_HardDiskControllerSCSI:
[16606]621 if (fIgnoreThis)
622 {
[18754]623 RTPrintf("%2u: SCSI controller, type %ls -- disabled\n",
[16606]624 a,
[50323]625 aVBoxValues[a]);
[16606]626 aEnabled[a] = false;
627 }
628 else
[16834]629 {
[18754]630 Utf8StrFmt strTypeArg("scsitype%u", a);
[16834]631 if (findArgValue(strOverride, pmapArgs, strTypeArg))
632 {
633 bstrFinalValue = strOverride;
[18754]634 RTPrintf("%2u: SCSI controller, type set with --unit %u --scsitype: \"%ls\"\n",
[16834]635 a,
636 a,
637 bstrFinalValue.raw());
638 }
639 else
[18754]640 RTPrintf("%2u: SCSI controller, type %ls"
641 "\n (change with \"--vsys %u --unit %u --scsitype {BusLogic|LsiLogic}\";"
642 "\n disable with \"--vsys %u --unit %u --ignore\")\n",
[16834]643 a,
[50323]644 aVBoxValues[a],
[16834]645 i, a, i, a);
646 }
[63300]647 break;
[16495]648
649 case VirtualSystemDescriptionType_HardDiskImage:
[16606]650 if (fIgnoreThis)
651 {
[18754]652 RTPrintf("%2u: Hard disk image: source image=%ls -- disabled\n",
[16606]653 a,
[17291]654 aOvfValues[a]);
[16606]655 aEnabled[a] = false;
656 }
657 else
[16834]658 {
[33361]659 Utf8StrFmt strTypeArg("disk%u", a);
[54979]660 RTCList<ImportOptions_T> optionsList = options.toList();
661
662 bstrFinalValue = aVBoxValues[a];
663
[16834]664 if (findArgValue(strOverride, pmapArgs, strTypeArg))
665 {
[54985]666 if (!optionsList.contains(ImportOptions_ImportToVDI))
[33361]667 {
[54979]668 RTUUID uuid;
669 /* Check if this is a uuid. If so, don't touch. */
670 int vrc = RTUuidFromStr(&uuid, strOverride.c_str());
671 if (vrc != VINF_SUCCESS)
[33361]672 {
[54979]673 /* Make the path absolute. */
674 if (!RTPathStartsWithRoot(strOverride.c_str()))
675 {
676 char pszPwd[RTPATH_MAX];
677 vrc = RTPathGetCurrent(pszPwd, RTPATH_MAX);
678 if (RT_SUCCESS(vrc))
679 strOverride = Utf8Str(pszPwd).append(RTPATH_SLASH).append(strOverride);
680 }
[33361]681 }
[54979]682 bstrFinalValue = strOverride;
[33361]683 }
[54979]684 else
685 {
686 //print some error about incompatible command-line arguments
687 return errorSyntax(USAGE_IMPORTAPPLIANCE,
688 "Option --ImportToVDI shall not be used together with "
689 "manually set target path.");
690
691 }
692
[18754]693 RTPrintf("%2u: Hard disk image: source image=%ls, target path=%ls, %ls\n",
[16834]694 a,
[17291]695 aOvfValues[a],
[33361]696 bstrFinalValue.raw(),
[16834]697 aExtraConfigValues[a]);
698 }
[33363]699#if 0 /* Changing the controller is fully valid, but the current design on how
700 the params are evaluated here doesn't allow two parameter for one
[33372]701 unit. The target disk path is more important I leave it for future
[33363]702 improvments. */
703 Utf8StrFmt strTypeArg("controller%u", a);
704 if (findArgValue(strOverride, pmapArgs, strTypeArg))
705 {
[33361]706 // strOverride now has the controller index as a number, but we
707 // need a "controller=X" format string
[33363]708 strOverride = Utf8StrFmt("controller=%s", strOverride.c_str());
709 Bstr bstrExtraConfigValue = strOverride;
710 bstrExtraConfigValue.detachTo(&aExtraConfigValues[a]);
711 RTPrintf("%2u: Hard disk image: source image=%ls, target path=%ls, %ls\n",
712 a,
713 aOvfValues[a],
[50323]714 aVBoxValues[a],
[33363]715 aExtraConfigValues[a]);
716 }
717#endif
[16834]718 else
[54979]719 {
720 strOverride = aVBoxValues[a];
721
722 /*
[54991]723 * Current solution isn't optimal.
[54979]724 * Better way is to provide API call for function
725 * Appliance::i_findMediumFormatFromDiskImage()
726 * and creating one new function which returns
727 * struct ovf::DiskImage for currently processed disk.
728 */
729
730 /*
731 * if user wants to convert all imported disks to VDI format
732 * we need to replace files extensions to "vdi"
733 * except CD/DVD disks
734 */
[54985]735 if (optionsList.contains(ImportOptions_ImportToVDI))
[54979]736 {
737 ComPtr<IVirtualBox> pVirtualBox = arg->virtualBox;
738 ComPtr<ISystemProperties> systemProperties;
739 com::SafeIfaceArray<IMediumFormat> mediumFormats;
740 Bstr bstrFormatName;
741
742 CHECK_ERROR(pVirtualBox,
743 COMGETTER(SystemProperties)(systemProperties.asOutParam()));
744
745 CHECK_ERROR(systemProperties,
746 COMGETTER(MediumFormats)(ComSafeArrayAsOutParam(mediumFormats)));
747
748 /* go through all supported media formats and store files extensions only for RAW */
749 com::SafeArray<BSTR> extensions;
750
[54991]751 for (unsigned j = 0; j < mediumFormats.size(); ++j)
[54979]752 {
753 com::SafeArray<DeviceType_T> deviceType;
[54991]754 ComPtr<IMediumFormat> mediumFormat = mediumFormats[j];
[54979]755 CHECK_ERROR(mediumFormat, COMGETTER(Name)(bstrFormatName.asOutParam()));
756 Utf8Str strFormatName = Utf8Str(bstrFormatName);
[54991]757
[54979]758 if (strFormatName.compare("RAW", Utf8Str::CaseInsensitive) == 0)
759 {
760 /* getting files extensions for "RAW" format */
761 CHECK_ERROR(mediumFormat,
762 DescribeFileExtensions(ComSafeArrayAsOutParam(extensions),
763 ComSafeArrayAsOutParam(deviceType)));
764 break;
765 }
766 }
767
768 /* go through files extensions for RAW format and compare them with
769 * extension of current file
770 */
[54991]771 bool fReplace = true;
[54979]772
773 const char *pszExtension = RTPathSuffix(strOverride.c_str());
774 if (pszExtension)
775 pszExtension++;
776
[54991]777 for (unsigned j = 0; j < extensions.size(); ++j)
[54979]778 {
[54991]779 Bstr bstrExt(extensions[j]);
[54983]780 Utf8Str strExtension(bstrExt);
[54979]781 if(strExtension.compare(pszExtension, Utf8Str::CaseInsensitive) == 0)
782 {
[54991]783 fReplace = false;
[54979]784 break;
785 }
786 }
787
[54991]788 if (fReplace)
[54979]789 {
790 strOverride = strOverride.stripSuffix();
791 strOverride = strOverride.append(".").append("vdi");
792 }
793 }
794
795 bstrFinalValue = strOverride;
796
[18754]797 RTPrintf("%2u: Hard disk image: source image=%ls, target path=%ls, %ls"
[33406]798 "\n (change target path with \"--vsys %u --unit %u --disk path\";"
[18754]799 "\n disable with \"--vsys %u --unit %u --ignore\")\n",
[16834]800 a,
[17291]801 aOvfValues[a],
[54979]802 bstrFinalValue.raw(),
[16834]803 aExtraConfigValues[a],
804 i, a, i, a);
[54979]805 }
[16834]806 }
[63300]807 break;
[16495]808
809 case VirtualSystemDescriptionType_CDROM:
[16606]810 if (fIgnoreThis)
811 {
[18754]812 RTPrintf("%2u: CD-ROM -- disabled\n",
[16606]813 a);
814 aEnabled[a] = false;
815 }
816 else
[18754]817 RTPrintf("%2u: CD-ROM"
818 "\n (disable with \"--vsys %u --unit %u --ignore\")\n",
[16606]819 a, i, a);
[63300]820 break;
[16495]821
822 case VirtualSystemDescriptionType_Floppy:
[16606]823 if (fIgnoreThis)
824 {
[18754]825 RTPrintf("%2u: Floppy -- disabled\n",
[16606]826 a);
827 aEnabled[a] = false;
828 }
829 else
[18754]830 RTPrintf("%2u: Floppy"
831 "\n (disable with \"--vsys %u --unit %u --ignore\")\n",
[16606]832 a, i, a);
[63300]833 break;
[16495]834
835 case VirtualSystemDescriptionType_NetworkAdapter:
[63567]836 RTPrintf("%2u: Network adapter: orig %ls, config %ls, extra %ls\n", /// @todo implement once we have a plan for the back-end
[16495]837 a,
[17291]838 aOvfValues[a],
[50323]839 aVBoxValues[a],
[16495]840 aExtraConfigValues[a]);
[63300]841 break;
[16495]842
843 case VirtualSystemDescriptionType_USBController:
[16606]844 if (fIgnoreThis)
845 {
[18754]846 RTPrintf("%2u: USB controller -- disabled\n",
[16606]847 a);
848 aEnabled[a] = false;
849 }
850 else
[18754]851 RTPrintf("%2u: USB controller"
852 "\n (disable with \"--vsys %u --unit %u --ignore\")\n",
[16606]853 a, i, a);
[63300]854 break;
[16495]855
856 case VirtualSystemDescriptionType_SoundCard:
[16606]857 if (fIgnoreThis)
858 {
[38525]859 RTPrintf("%2u: Sound card \"%ls\" -- disabled\n",
[16606]860 a,
[17291]861 aOvfValues[a]);
[16606]862 aEnabled[a] = false;
863 }
864 else
[18754]865 RTPrintf("%2u: Sound card (appliance expects \"%ls\", can change on import)"
866 "\n (disable with \"--vsys %u --unit %u --ignore\")\n",
[16606]867 a,
[17291]868 aOvfValues[a],
[16606]869 i,
870 a);
[63300]871 break;
872
873 case VirtualSystemDescriptionType_SettingsFile:
[72476]874 if (findArgValue(strOverride, pmapArgs, "settingsfile"))
875 {
876 bstrFinalValue = strOverride;
877 RTPrintf("%2u: VM settings file name specified with --settingsfile: \"%ls\"\n",
878 a, bstrFinalValue.raw());
879 }
880 else
881 RTPrintf("%2u: Suggested VM settings file name \"%ls\""
882 "\n (change with \"--vsys %u --settingsfile <filename>\")\n",
883 a, bstrFinalValue.raw(), i);
[63300]884 break;
[72476]885
886 case VirtualSystemDescriptionType_BaseFolder:
887 if (findArgValue(strOverride, pmapArgs, "basefolder"))
888 {
889 bstrFinalValue = strOverride;
890 RTPrintf("%2u: VM base folder specified with --basefolder: \"%ls\"\n",
891 a, bstrFinalValue.raw());
892 }
893 else
894 RTPrintf("%2u: Suggested VM base folder \"%ls\""
895 "\n (change with \"--vsys %u --basefolder <path>\")\n",
896 a, bstrFinalValue.raw(), i);
897 break;
898
899 case VirtualSystemDescriptionType_PrimaryGroup:
900 if (findArgValue(strOverride, pmapArgs, "group"))
901 {
902 bstrFinalValue = strOverride;
903 RTPrintf("%2u: VM group specified with --group: \"%ls\"\n",
904 a, bstrFinalValue.raw());
905 }
906 else
907 RTPrintf("%2u: Suggested VM group \"%ls\""
908 "\n (change with \"--vsys %u --group <group>\")\n",
909 a, bstrFinalValue.raw(), i);
910 break;
911
[76192]912 case VirtualSystemDescriptionType_CloudInstanceShape:
913 case VirtualSystemDescriptionType_CloudDomain:
914 case VirtualSystemDescriptionType_CloudBootDiskSize:
915 case VirtualSystemDescriptionType_CloudBucket:
[73217]916 case VirtualSystemDescriptionType_CloudOCIVCN:
[76192]917 case VirtualSystemDescriptionType_CloudPublicIP:
918 case VirtualSystemDescriptionType_CloudProfileName:
[73929]919 case VirtualSystemDescriptionType_CloudOCISubnet:
[76192]920 case VirtualSystemDescriptionType_CloudKeepObject:
921 case VirtualSystemDescriptionType_CloudLaunchInstance:
[63300]922 case VirtualSystemDescriptionType_Miscellaneous:
923 /** @todo VirtualSystemDescriptionType_Miscellaneous? */
924 break;
[76192]925
[63300]926 case VirtualSystemDescriptionType_Ignore:
[72976]927#ifdef VBOX_WITH_XPCOM_CPP_ENUM_HACK
928 case VirtualSystemDescriptionType_32BitHack:
929#endif
[63300]930 break;
[16495]931 }
[16601]932
933 bstrFinalValue.detachTo(&aFinalValues[a]);
[16495]934 }
[16517]935
[16601]936 if (fExecute)
937 CHECK_ERROR_BREAK(aVirtualSystemDescriptions[i],
938 SetFinalValues(ComSafeArrayAsInParam(aEnabled),
[16668]939 ComSafeArrayAsInParam(aFinalValues),
940 ComSafeArrayAsInParam(aExtraConfigValues)));
[16601]941
942 } // for (unsigned i = 0; i < cVirtualSystemDescriptions; ++i)
943
[18223]944 if (cLicensesInTheWay == 1)
[32701]945 RTMsgError("Cannot import until the license agreement listed above is accepted.");
[18223]946 else if (cLicensesInTheWay > 1)
[32701]947 RTMsgError("Cannot import until the %c license agreements listed above are accepted.", cLicensesInTheWay);
[18223]948
949 if (!cLicensesInTheWay && fExecute)
[16517]950 {
[17827]951 // go!
[16517]952 ComPtr<IProgress> progress;
[17033]953 CHECK_ERROR_BREAK(pAppliance,
[37862]954 ImportMachines(ComSafeArrayAsInParam(options), progress.asOutParam()));
[16517]955
[21612]956 rc = showProgress(progress);
[38525]957 CHECK_PROGRESS_ERROR_RET(progress, ("Appliance import failed"), RTEXITCODE_FAILURE);
[16517]958
[38525]959 if (SUCCEEDED(rc))
[16581]960 RTPrintf("Successfully imported the appliance.\n");
[16517]961 }
962 } // end if (aVirtualSystemDescriptions.size() > 0)
[16495]963 } while (0);
964
[38525]965 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
[16491]966}
967
[50447]968static int parseExportOptions(const char *psz, com::SafeArray<ExportOptions_T> *options)
969{
970 int rc = VINF_SUCCESS;
971 while (psz && *psz && RT_SUCCESS(rc))
972 {
973 size_t len;
974 const char *pszComma = strchr(psz, ',');
975 if (pszComma)
976 len = pszComma - psz;
977 else
978 len = strlen(psz);
979 if (len > 0)
980 {
981 if (!RTStrNICmp(psz, "CreateManifest", len))
982 options->push_back(ExportOptions_CreateManifest);
983 else if (!RTStrNICmp(psz, "manifest", len))
984 options->push_back(ExportOptions_CreateManifest);
985 else if (!RTStrNICmp(psz, "ExportDVDImages", len))
986 options->push_back(ExportOptions_ExportDVDImages);
987 else if (!RTStrNICmp(psz, "iso", len))
988 options->push_back(ExportOptions_ExportDVDImages);
989 else if (!RTStrNICmp(psz, "StripAllMACs", len))
990 options->push_back(ExportOptions_StripAllMACs);
991 else if (!RTStrNICmp(psz, "nomacs", len))
992 options->push_back(ExportOptions_StripAllMACs);
993 else if (!RTStrNICmp(psz, "StripAllNonNATMACs", len))
994 options->push_back(ExportOptions_StripAllNonNATMACs);
995 else if (!RTStrNICmp(psz, "nomacsbutnat", len))
996 options->push_back(ExportOptions_StripAllNonNATMACs);
997 else
998 rc = VERR_PARSE_ERROR;
999 }
1000 if (pszComma)
1001 psz += len + 1;
1002 else
1003 psz += len;
1004 }
[17098]1005
[50447]1006 return rc;
1007}
1008
1009static const RTGETOPTDEF g_aExportOptions[] =
1010{
1011 { "--output", 'o', RTGETOPT_REQ_STRING },
1012 { "--legacy09", 'l', RTGETOPT_REQ_NOTHING },
1013 { "--ovf09", 'l', RTGETOPT_REQ_NOTHING },
1014 { "--ovf10", '1', RTGETOPT_REQ_NOTHING },
1015 { "--ovf20", '2', RTGETOPT_REQ_NOTHING },
[67208]1016 { "--opc10", 'c', RTGETOPT_REQ_NOTHING },
[50447]1017 { "--manifest", 'm', RTGETOPT_REQ_NOTHING }, // obsoleted by --options
1018 { "--iso", 'I', RTGETOPT_REQ_NOTHING }, // obsoleted by --options
1019 { "--vsys", 's', RTGETOPT_REQ_UINT32 },
[72476]1020 { "--vmname", 'V', RTGETOPT_REQ_STRING },
[50447]1021 { "--product", 'p', RTGETOPT_REQ_STRING },
1022 { "--producturl", 'P', RTGETOPT_REQ_STRING },
1023 { "--vendor", 'n', RTGETOPT_REQ_STRING },
1024 { "--vendorurl", 'N', RTGETOPT_REQ_STRING },
1025 { "--version", 'v', RTGETOPT_REQ_STRING },
1026 { "--description", 'd', RTGETOPT_REQ_STRING },
1027 { "--eula", 'e', RTGETOPT_REQ_STRING },
1028 { "--eulafile", 'E', RTGETOPT_REQ_STRING },
1029 { "--options", 'O', RTGETOPT_REQ_STRING },
[75920]1030 { "--cloud", 'C', RTGETOPT_REQ_UINT32 },
1031 { "--cloudshape", 'S', RTGETOPT_REQ_STRING },
1032 { "--clouddomain", 'D', RTGETOPT_REQ_STRING },
1033 { "--clouddisksize", 'R', RTGETOPT_REQ_STRING },
1034 { "--cloudbucket", 'B', RTGETOPT_REQ_STRING },
1035 { "--cloudocivcn", 'Q', RTGETOPT_REQ_STRING },
1036 { "--cloudpublicip", 'A', RTGETOPT_REQ_STRING },
1037 { "--cloudprofile", 'F', RTGETOPT_REQ_STRING },
1038 { "--cloudocisubnet", 'T', RTGETOPT_REQ_STRING },
1039 { "--cloudkeepobject", 'K', RTGETOPT_REQ_STRING },
1040 { "--cloudlaunchinstance", 'L', RTGETOPT_REQ_STRING },
[50447]1041};
1042
[75920]1043enum
1044{
1045 NOT_SET, LOCAL, CLOUD
1046} exportType;
1047
[56118]1048RTEXITCODE handleExportAppliance(HandlerArg *a)
[17033]1049{
1050 HRESULT rc = S_OK;
1051
1052 Utf8Str strOutputFile;
[18809]1053 Utf8Str strOvfFormat("ovf-1.0"); // the default export version
[32448]1054 bool fManifest = false; // the default
[75920]1055 bool fCloud = false; // the default
1056 exportType = NOT_SET;
[49103]1057 bool fExportISOImages = false; // the default
[50447]1058 com::SafeArray<ExportOptions_T> options;
[17033]1059 std::list< ComPtr<IMachine> > llMachines;
1060
[18809]1061 uint32_t ulCurVsys = (uint32_t)-1;
1062 // for each --vsys X command, maintain a map of command line items
1063 ArgsMapsMap mapArgsMapsPerVsys;
[17033]1064 do
1065 {
[17098]1066 int c;
1067
1068 RTGETOPTUNION ValueUnion;
1069 RTGETOPTSTATE GetState;
[18108]1070 // start at 0 because main() has hacked both the argc and argv given to us
1071 RTGetOptInit(&GetState, a->argc, a->argv, g_aExportOptions,
[26517]1072 RT_ELEMENTS(g_aExportOptions), 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
[18809]1073
1074 Utf8Str strProductUrl;
[17098]1075 while ((c = RTGetOpt(&GetState, &ValueUnion)))
[17033]1076 {
[17098]1077 switch (c)
[17033]1078 {
[17098]1079 case 'o': // --output
[17033]1080 if (strOutputFile.length())
1081 return errorSyntax(USAGE_EXPORTAPPLIANCE, "You can only specify --output once.");
1082 else
[17098]1083 strOutputFile = ValueUnion.psz;
[40329]1084 break;
[17098]1085
[40329]1086 case 'l': // --legacy09/--ovf09
[50447]1087 strOvfFormat = "ovf-0.9";
1088 break;
[18809]1089
[40329]1090 case '1': // --ovf10
[50447]1091 strOvfFormat = "ovf-1.0";
1092 break;
[40329]1093
1094 case '2': // --ovf20
[50447]1095 strOvfFormat = "ovf-2.0";
1096 break;
[40329]1097
[67194]1098 case 'c': // --opc
[67208]1099 strOvfFormat = "opc-1.0";
[67194]1100 break;
1101
[49103]1102 case 'I': // --iso
[50447]1103 fExportISOImages = true;
1104 break;
[49103]1105
[32448]1106 case 'm': // --manifest
[50447]1107 fManifest = true;
1108 break;
[32448]1109
[18809]1110 case 's': // --vsys
[75920]1111 if (fCloud == false && exportType == NOT_SET)
1112 exportType = LOCAL;
1113
1114 if (exportType != LOCAL)
1115 return errorSyntax(USAGE_EXPORTAPPLIANCE,
1116 "Option \"%s\" can't be used together with \"--cloud\" argument.",
1117 GetState.pDef->pszLong);
1118
[50447]1119 ulCurVsys = ValueUnion.u32;
1120 break;
[18809]1121
[72476]1122 case 'V': // --vmname
[75920]1123 if (exportType == NOT_SET || ulCurVsys == (uint32_t)-1)
[76192]1124 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys or --cloud argument.",
1125 GetState.pDef->pszLong);
[72476]1126 mapArgsMapsPerVsys[ulCurVsys]["vmname"] = ValueUnion.psz;
1127 break;
1128
[18809]1129 case 'p': // --product
[75920]1130 if (exportType != LOCAL)
[50447]1131 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
1132 mapArgsMapsPerVsys[ulCurVsys]["product"] = ValueUnion.psz;
1133 break;
[18809]1134
1135 case 'P': // --producturl
[75920]1136 if (exportType != LOCAL)
[50447]1137 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
1138 mapArgsMapsPerVsys[ulCurVsys]["producturl"] = ValueUnion.psz;
1139 break;
[18809]1140
[46290]1141 case 'n': // --vendor
[75920]1142 if (exportType != LOCAL)
[50447]1143 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
1144 mapArgsMapsPerVsys[ulCurVsys]["vendor"] = ValueUnion.psz;
1145 break;
[18809]1146
[46290]1147 case 'N': // --vendorurl
[75920]1148 if (exportType != LOCAL)
[50447]1149 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
1150 mapArgsMapsPerVsys[ulCurVsys]["vendorurl"] = ValueUnion.psz;
1151 break;
[18809]1152
1153 case 'v': // --version
[75920]1154 if (exportType != LOCAL)
[50447]1155 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
1156 mapArgsMapsPerVsys[ulCurVsys]["version"] = ValueUnion.psz;
1157 break;
[18809]1158
[46290]1159 case 'd': // --description
[75920]1160 if (exportType != LOCAL)
[50447]1161 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
1162 mapArgsMapsPerVsys[ulCurVsys]["description"] = ValueUnion.psz;
1163 break;
[46290]1164
[18809]1165 case 'e': // --eula
[75920]1166 if (exportType != LOCAL)
[50447]1167 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
1168 mapArgsMapsPerVsys[ulCurVsys]["eula"] = ValueUnion.psz;
1169 break;
[18809]1170
1171 case 'E': // --eulafile
[75920]1172 if (exportType != LOCAL)
[50447]1173 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
1174 mapArgsMapsPerVsys[ulCurVsys]["eulafile"] = ValueUnion.psz;
1175 break;
[18809]1176
[50447]1177 case 'O': // --options
1178 if (RT_FAILURE(parseExportOptions(ValueUnion.psz, &options)))
1179 return errorArgument("Invalid export options '%s'\n", ValueUnion.psz);
1180 break;
1181
[75920]1182 /*--cloud and --vsys are orthogonal, only one must be presented*/
1183 case 'C': // --cloud
1184 if (fCloud == false && exportType == NOT_SET)
1185 {
1186 fCloud = true;
1187 exportType = CLOUD;
1188 }
1189
1190 if (exportType != CLOUD)
1191 return errorSyntax(USAGE_EXPORTAPPLIANCE,
1192 "Option \"%s\" can't be used together with \"--vsys\" argument.",
1193 GetState.pDef->pszLong);
1194
1195 ulCurVsys = ValueUnion.u32;
1196 break;
1197
1198 /* Cloud export settings */
1199 case 'S': // --cloudshape
1200 if (exportType != CLOUD)
1201 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --cloud argument.",
1202 GetState.pDef->pszLong);
1203 mapArgsMapsPerVsys[ulCurVsys]["cloudshape"] = ValueUnion.psz;
1204 break;
1205
1206 case 'D': // --clouddomain
1207 if (exportType != CLOUD)
1208 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --cloud argument.",
1209 GetState.pDef->pszLong);
1210 mapArgsMapsPerVsys[ulCurVsys]["clouddomain"] = ValueUnion.psz;
1211 break;
1212
1213 case 'R': // --clouddisksize
1214 if (exportType != CLOUD)
1215 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --cloud argument.",
1216 GetState.pDef->pszLong);
1217 mapArgsMapsPerVsys[ulCurVsys]["clouddisksize"] = ValueUnion.psz;
1218 break;
1219
1220 case 'B': // --cloudbucket
1221 if (exportType != CLOUD)
1222 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --cloud argument.",
1223 GetState.pDef->pszLong);
1224 mapArgsMapsPerVsys[ulCurVsys]["cloudbucket"] = ValueUnion.psz;
1225 break;
1226
1227 case 'Q': // --cloudocivcn
1228 if (exportType != CLOUD)
1229 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --cloud argument.",
1230 GetState.pDef->pszLong);
1231 mapArgsMapsPerVsys[ulCurVsys]["cloudocivcn"] = ValueUnion.psz;
1232 break;
1233
1234 case 'A': // --cloudpublicip
1235 if (exportType != CLOUD)
1236 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --cloud argument.",
1237 GetState.pDef->pszLong);
1238 mapArgsMapsPerVsys[ulCurVsys]["cloudpublicip"] = ValueUnion.psz;
1239 break;
1240
1241 case 'F': // --cloudprofile
1242 if (exportType != CLOUD)
1243 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --cloud argument.",
1244 GetState.pDef->pszLong);
1245 mapArgsMapsPerVsys[ulCurVsys]["cloudprofile"] = ValueUnion.psz;
1246 break;
1247
1248 case 'T': // --cloudocisubnet
1249 if (exportType != CLOUD)
1250 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --cloud argument.",
1251 GetState.pDef->pszLong);
1252 mapArgsMapsPerVsys[ulCurVsys]["cloudocisubnet"] = ValueUnion.psz;
1253 break;
1254
1255 case 'K': // --cloudkeepobject
1256 if (exportType != CLOUD)
1257 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --cloud argument.",
1258 GetState.pDef->pszLong);
1259 mapArgsMapsPerVsys[ulCurVsys]["cloudkeepobject"] = ValueUnion.psz;
1260 break;
1261
1262 case 'L': // --cloudlaunchinstance
1263 if (exportType != CLOUD)
1264 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --cloud argument.",
1265 GetState.pDef->pszLong);
1266 mapArgsMapsPerVsys[ulCurVsys]["cloudlaunchinstance"] = ValueUnion.psz;
1267 break;
1268
[17098]1269 case VINF_GETOPT_NOT_OPTION:
[17033]1270 {
[17098]1271 Utf8Str strMachine(ValueUnion.psz);
1272 // must be machine: try UUID or name
1273 ComPtr<IMachine> machine;
[33294]1274 CHECK_ERROR_BREAK(a->virtualBox, FindMachine(Bstr(strMachine).raw(),
1275 machine.asOutParam()));
[17098]1276 if (machine)
1277 llMachines.push_back(machine);
[40329]1278 break;
[17033]1279 }
1280
[17098]1281 default:
1282 if (c > 0)
[18108]1283 {
1284 if (RT_C_IS_GRAPH(c))
1285 return errorSyntax(USAGE_EXPORTAPPLIANCE, "unhandled option: -%c", c);
1286 else
1287 return errorSyntax(USAGE_EXPORTAPPLIANCE, "unhandled option: %i", c);
1288 }
1289 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
1290 return errorSyntax(USAGE_EXPORTAPPLIANCE, "unknown option: %s", ValueUnion.psz);
[17098]1291 else if (ValueUnion.pDef)
[18108]1292 return errorSyntax(USAGE_EXPORTAPPLIANCE, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
[17098]1293 else
[18108]1294 return errorSyntax(USAGE_EXPORTAPPLIANCE, "%Rrs", c);
[17033]1295 }
[17361]1296
1297 if (FAILED(rc))
1298 break;
[17033]1299 }
1300
[17074]1301 if (FAILED(rc))
1302 break;
1303
[72823]1304 if (llMachines.empty())
[17098]1305 return errorSyntax(USAGE_EXPORTAPPLIANCE, "At least one machine must be specified with the export command.");
1306 if (!strOutputFile.length())
1307 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Missing --output argument with export command.");
[17033]1308
[18809]1309 // match command line arguments with the machines count
1310 // this is only to sort out invalid indices at this time
1311 ArgsMapsMap::const_iterator it;
1312 for (it = mapArgsMapsPerVsys.begin();
1313 it != mapArgsMapsPerVsys.end();
1314 ++it)
1315 {
1316 uint32_t ulVsys = it->first;
1317 if (ulVsys >= llMachines.size())
1318 return errorSyntax(USAGE_EXPORTAPPLIANCE,
1319 "Invalid index %RI32 with -vsys option; you specified only %zu virtual system(s).",
1320 ulVsys, llMachines.size());
1321 }
1322
[17033]1323 ComPtr<IAppliance> pAppliance;
1324 CHECK_ERROR_BREAK(a->virtualBox, CreateAppliance(pAppliance.asOutParam()));
1325
[33417]1326 char *pszAbsFilePath = 0;
[36527]1327 if (strOutputFile.startsWith("S3://", RTCString::CaseInsensitive) ||
1328 strOutputFile.startsWith("SunCloud://", RTCString::CaseInsensitive) ||
[75920]1329 strOutputFile.startsWith("webdav://", RTCString::CaseInsensitive) ||
1330 strOutputFile.startsWith("OCI://", RTCString::CaseInsensitive))
[33417]1331 pszAbsFilePath = RTStrDup(strOutputFile.c_str());
1332 else
1333 pszAbsFilePath = RTPathAbsDup(strOutputFile.c_str());
1334
[17033]1335 std::list< ComPtr<IMachine> >::iterator itM;
[18809]1336 uint32_t i=0;
[17033]1337 for (itM = llMachines.begin();
1338 itM != llMachines.end();
[18809]1339 ++itM, ++i)
[17033]1340 {
1341 ComPtr<IMachine> pMachine = *itM;
[18214]1342 ComPtr<IVirtualSystemDescription> pVSD;
[45068]1343 CHECK_ERROR_BREAK(pMachine, ExportTo(pAppliance, Bstr(pszAbsFilePath).raw(), pVSD.asOutParam()));
[75920]1344
[33540]1345 // Add additional info to the virtual system description if the user wants so
[18809]1346 ArgsMap *pmapArgs = NULL;
1347 ArgsMapsMap::iterator itm = mapArgsMapsPerVsys.find(i);
1348 if (itm != mapArgsMapsPerVsys.end())
1349 pmapArgs = &itm->second;
1350 if (pmapArgs)
1351 {
1352 ArgsMap::iterator itD;
1353 for (itD = pmapArgs->begin();
1354 itD != pmapArgs->end();
1355 ++itD)
1356 {
[72476]1357 if (itD->first == "vmname")
[75920]1358 {
1359 //remove default value if user has specified new name (default value is set in the ExportTo())
1360 pVSD->RemoveDescriptionByType(VirtualSystemDescriptionType_Name);
[72476]1361 pVSD->AddDescription(VirtualSystemDescriptionType_Name,
1362 Bstr(itD->second).raw(),
1363 Bstr(itD->second).raw());
[75920]1364 }
[72476]1365 else if (itD->first == "product")
[32718]1366 pVSD->AddDescription(VirtualSystemDescriptionType_Product,
1367 Bstr(itD->second).raw(),
1368 Bstr(itD->second).raw());
[18809]1369 else if (itD->first == "producturl")
[32718]1370 pVSD->AddDescription(VirtualSystemDescriptionType_ProductUrl,
1371 Bstr(itD->second).raw(),
1372 Bstr(itD->second).raw());
[18809]1373 else if (itD->first == "vendor")
[32718]1374 pVSD->AddDescription(VirtualSystemDescriptionType_Vendor,
1375 Bstr(itD->second).raw(),
1376 Bstr(itD->second).raw());
[18809]1377 else if (itD->first == "vendorurl")
[32718]1378 pVSD->AddDescription(VirtualSystemDescriptionType_VendorUrl,
1379 Bstr(itD->second).raw(),
1380 Bstr(itD->second).raw());
[18809]1381 else if (itD->first == "version")
[32718]1382 pVSD->AddDescription(VirtualSystemDescriptionType_Version,
1383 Bstr(itD->second).raw(),
1384 Bstr(itD->second).raw());
[46290]1385 else if (itD->first == "description")
1386 pVSD->AddDescription(VirtualSystemDescriptionType_Description,
1387 Bstr(itD->second).raw(),
1388 Bstr(itD->second).raw());
[18809]1389 else if (itD->first == "eula")
[32718]1390 pVSD->AddDescription(VirtualSystemDescriptionType_License,
1391 Bstr(itD->second).raw(),
1392 Bstr(itD->second).raw());
[18809]1393 else if (itD->first == "eulafile")
1394 {
1395 Utf8Str strContent;
1396 void *pvFile;
1397 size_t cbFile;
[24998]1398 int irc = RTFileReadAll(itD->second.c_str(), &pvFile, &cbFile);
1399 if (RT_SUCCESS(irc))
[18809]1400 {
[44868]1401 Bstr bstrContent((char*)pvFile, cbFile);
[32718]1402 pVSD->AddDescription(VirtualSystemDescriptionType_License,
1403 bstrContent.raw(),
1404 bstrContent.raw());
[18809]1405 RTFileReadAllFree(pvFile, cbFile);
1406 }
1407 else
1408 {
[32701]1409 RTMsgError("Cannot read license file \"%s\" which should be included in the virtual system %u.",
1410 itD->second.c_str(), i);
[56118]1411 return RTEXITCODE_FAILURE;
[18809]1412 }
1413 }
[75920]1414 /* add cloud export settings */
1415 else if (itD->first == "cloudshape")
[76192]1416 pVSD->AddDescription(VirtualSystemDescriptionType_CloudInstanceShape,
[75920]1417 Bstr(itD->second).raw(),
1418 Bstr(itD->second).raw());
1419 else if (itD->first == "clouddomain")
[76192]1420 pVSD->AddDescription(VirtualSystemDescriptionType_CloudDomain,
[75920]1421 Bstr(itD->second).raw(),
1422 Bstr(itD->second).raw());
1423 else if (itD->first == "clouddisksize")
[76192]1424 pVSD->AddDescription(VirtualSystemDescriptionType_CloudBootDiskSize,
[75920]1425 Bstr(itD->second).raw(),
1426 Bstr(itD->second).raw());
1427 else if (itD->first == "cloudbucket")
[76192]1428 pVSD->AddDescription(VirtualSystemDescriptionType_CloudBucket,
[75920]1429 Bstr(itD->second).raw(),
1430 Bstr(itD->second).raw());
1431 else if (itD->first == "cloudocivcn")
1432 pVSD->AddDescription(VirtualSystemDescriptionType_CloudOCIVCN,
1433 Bstr(itD->second).raw(),
1434 Bstr(itD->second).raw());
1435 else if (itD->first == "cloudpublicip")
[76192]1436 pVSD->AddDescription(VirtualSystemDescriptionType_CloudPublicIP,
[75920]1437 Bstr(itD->second).raw(),
1438 Bstr(itD->second).raw());
1439 else if (itD->first == "cloudprofile")
[76192]1440 pVSD->AddDescription(VirtualSystemDescriptionType_CloudProfileName,
[75920]1441 Bstr(itD->second).raw(),
1442 Bstr(itD->second).raw());
1443 else if (itD->first == "cloudocisubnet")
1444 pVSD->AddDescription(VirtualSystemDescriptionType_CloudOCISubnet,
1445 Bstr(itD->second).raw(),
1446 Bstr(itD->second).raw());
1447 else if (itD->first == "cloudkeepobject")
[76192]1448 pVSD->AddDescription(VirtualSystemDescriptionType_CloudKeepObject,
[75920]1449 Bstr(itD->second).raw(),
1450 Bstr(itD->second).raw());
1451 else if (itD->first == "cloudlaunchinstance")
[76192]1452 pVSD->AddDescription(VirtualSystemDescriptionType_CloudLaunchInstance,
[75920]1453 Bstr(itD->second).raw(),
1454 Bstr(itD->second).raw());
[18809]1455 }
1456 }
[17033]1457 }
1458
1459 if (FAILED(rc))
1460 break;
1461
[55182]1462 /* Query required passwords and supply them to the appliance. */
1463 com::SafeArray<BSTR> aIdentifiers;
1464
1465 CHECK_ERROR_BREAK(pAppliance, GetPasswordIds(ComSafeArrayAsOutParam(aIdentifiers)));
1466
1467 if (aIdentifiers.size() > 0)
1468 {
1469 com::SafeArray<BSTR> aPasswords(aIdentifiers.size());
1470 RTPrintf("Enter the passwords for the following identifiers to export the apppliance:\n");
1471 for (unsigned idxId = 0; idxId < aIdentifiers.size(); idxId++)
1472 {
1473 com::Utf8Str strPassword;
1474 Bstr bstrPassword;
1475 Bstr bstrId = aIdentifiers[idxId];
1476
1477 RTEXITCODE rcExit = readPasswordFromConsole(&strPassword, "Password ID %s:", Utf8Str(bstrId).c_str());
1478 if (rcExit == RTEXITCODE_FAILURE)
[55268]1479 {
1480 RTStrFree(pszAbsFilePath);
[55182]1481 return rcExit;
[55268]1482 }
[55182]1483
1484 bstrPassword = strPassword;
1485 bstrPassword.detachTo(&aPasswords[idxId]);
1486 }
1487
1488 CHECK_ERROR_BREAK(pAppliance, AddPasswords(ComSafeArrayAsInParam(aIdentifiers),
1489 ComSafeArrayAsInParam(aPasswords)));
1490 }
1491
[49038]1492 if (fManifest)
1493 options.push_back(ExportOptions_CreateManifest);
1494
[49103]1495 if (fExportISOImages)
1496 options.push_back(ExportOptions_ExportDVDImages);
1497
[17287]1498 ComPtr<IProgress> progress;
[32718]1499 CHECK_ERROR_BREAK(pAppliance, Write(Bstr(strOvfFormat).raw(),
[49038]1500 ComSafeArrayAsInParam(options),
[32718]1501 Bstr(pszAbsFilePath).raw(),
1502 progress.asOutParam()));
[18775]1503 RTStrFree(pszAbsFilePath);
[17033]1504
[21612]1505 rc = showProgress(progress);
[38525]1506 CHECK_PROGRESS_ERROR_RET(progress, ("Appliance write failed"), RTEXITCODE_FAILURE);
[17287]1507
[38525]1508 if (SUCCEEDED(rc))
[17287]1509 RTPrintf("Successfully exported %d machine(s).\n", llMachines.size());
1510
[17033]1511 } while (0);
1512
[38525]1513 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
[17033]1514}
1515
[16491]1516#endif /* !VBOX_ONLY_DOCS */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use