VirtualBox

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

Last change on this file since 78841 was 78841, checked in by vboxsync, 6 years ago

bugref:9436. Added progress for getInstanceInfo. Now getInstanceInfo is run in async way.

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette