VirtualBox

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

Last change on this file since 76678 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
Line 
1/* $Id: VBoxManageAppliance.cpp 76553 2019-01-01 01:45:53Z 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
150RTEXITCODE handleImportAppliance(HandlerArg *arg)
151{
152 HRESULT rc = S_OK;
153
154 Utf8Str strOvfFilename;
155 bool fExecute = true; // if true, then we actually do the import
156 com::SafeArray<ImportOptions_T> options;
157 uint32_t ulCurVsys = (uint32_t)-1;
158 uint32_t ulCurUnit = (uint32_t)-1;
159 // for each --vsys X command, maintain a map of command line items
160 // (we'll parse them later after interpreting the OVF, when we can
161 // actually check whether they make sense semantically)
162 ArgsMapsMap mapArgsMapsPerVsys;
163 IgnoresMapsMap mapIgnoresMapsPerVsys;
164
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
169 RTGetOptInit(&GetState, arg->argc, arg->argv, g_aImportApplianceOptions, RT_ELEMENTS(g_aImportApplianceOptions),
170 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
171 while ((c = RTGetOpt(&GetState, &ValueUnion)))
172 {
173 switch (c)
174 {
175 case 'n': // --dry-run
176 fExecute = false;
177 break;
178
179 case 'P': // --detailed-progress
180 g_fDetailedProgress = true;
181 break;
182
183 case 's': // --vsys
184 ulCurVsys = ValueUnion.u32;
185 ulCurUnit = (uint32_t)-1;
186 break;
187
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
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
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
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
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
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
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
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
283 case VINF_GETOPT_NOT_OPTION:
284 if (strOvfFilename.isEmpty())
285 strOvfFilename = ValueUnion.psz;
286 else
287 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Invalid parameter '%s'", ValueUnion.psz);
288 break;
289
290 default:
291 if (c > 0)
292 {
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);
297 }
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);
304 }
305 }
306
307 if (strOvfFilename.isEmpty())
308 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Not enough arguments for \"import\" command.");
309
310 do
311 {
312 ComPtr<IAppliance> pAppliance;
313 CHECK_ERROR_BREAK(arg->virtualBox, CreateAppliance(pAppliance.asOutParam()));
314
315 char *pszAbsFilePath;
316 if (strOvfFilename.startsWith("S3://", RTCString::CaseInsensitive) ||
317 strOvfFilename.startsWith("SunCloud://", RTCString::CaseInsensitive) ||
318 strOvfFilename.startsWith("webdav://", RTCString::CaseInsensitive))
319 pszAbsFilePath = RTStrDup(strOvfFilename.c_str());
320 else
321 pszAbsFilePath = RTPathAbsDup(strOvfFilename.c_str());
322 ComPtr<IProgress> progressRead;
323 CHECK_ERROR_BREAK(pAppliance, Read(Bstr(pszAbsFilePath).raw(),
324 progressRead.asOutParam()));
325 RTStrFree(pszAbsFilePath);
326
327 rc = showProgress(progressRead);
328 CHECK_PROGRESS_ERROR_RET(progressRead, ("Appliance read failed"), RTEXITCODE_FAILURE);
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()));
332 // call interpret(); this can yield both warnings and errors, so we need
333 // to tinker with the error info a bit
334 RTStrmPrintf(g_pStdErr, "Interpreting %ls...\n", path.raw());
335 rc = pAppliance->Interpret();
336 com::ErrorInfo info0(pAppliance, COM_IIDOF(IAppliance));
337
338 com::SafeArray<BSTR> aWarnings;
339 if (SUCCEEDED(pAppliance->GetWarnings(ComSafeArrayAsOutParam(aWarnings))))
340 {
341 size_t cWarnings = aWarnings.size();
342 for (unsigned i = 0; i < cWarnings; ++i)
343 {
344 Bstr bstrWarning(aWarnings[i]);
345 RTMsgWarning("%ls.", bstrWarning.raw());
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
356 RTStrmPrintf(g_pStdErr, "OK.\n");
357
358 // fetch all disks
359 com::SafeArray<BSTR> retDisks;
360 CHECK_ERROR_BREAK(pAppliance,
361 COMGETTER(Disks)(ComSafeArrayAsOutParam(retDisks)));
362 if (retDisks.size() > 0)
363 {
364 RTPrintf("Disks:\n");
365 for (unsigned i = 0; i < retDisks.size(); i++)
366 RTPrintf(" %ls\n", retDisks[i]);
367 RTPrintf("\n");
368 }
369
370 // fetch virtual system descriptions
371 com::SafeIfaceArray<IVirtualSystemDescription> aVirtualSystemDescriptions;
372 CHECK_ERROR_BREAK(pAppliance,
373 COMGETTER(VirtualSystemDescriptions)(ComSafeArrayAsOutParam(aVirtualSystemDescriptions)));
374
375 size_t cVirtualSystemDescriptions = aVirtualSystemDescriptions.size();
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)
383 {
384 uint32_t ulVsys = it->first;
385 if (ulVsys >= cVirtualSystemDescriptions)
386 return errorSyntax(USAGE_IMPORTAPPLIANCE,
387 "Invalid index %RI32 with -vsys option; the OVF contains only %zu virtual system(s).",
388 ulVsys, cVirtualSystemDescriptions);
389 }
390
391 uint32_t cLicensesInTheWay = 0;
392
393 // dump virtual system descriptions and match command-line arguments
394 if (cVirtualSystemDescriptions > 0)
395 {
396 for (unsigned i = 0; i < cVirtualSystemDescriptions; ++i)
397 {
398 com::SafeArray<VirtualSystemDescriptionType_T> retTypes;
399 com::SafeArray<BSTR> aRefs;
400 com::SafeArray<BSTR> aOvfValues;
401 com::SafeArray<BSTR> aVBoxValues;
402 com::SafeArray<BSTR> aExtraConfigValues;
403 CHECK_ERROR_BREAK(aVirtualSystemDescriptions[i],
404 GetDescription(ComSafeArrayAsOutParam(retTypes),
405 ComSafeArrayAsOutParam(aRefs),
406 ComSafeArrayAsOutParam(aOvfValues),
407 ComSafeArrayAsOutParam(aVBoxValues),
408 ComSafeArrayAsOutParam(aExtraConfigValues)));
409
410 RTPrintf("Virtual system %u:\n", i);
411
412 // look up the corresponding command line options, if any
413 ArgsMap *pmapArgs = NULL;
414 ArgsMapsMap::iterator itm = mapArgsMapsPerVsys.find(i);
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
422 for (unsigned a = 0; a < retTypes.size(); ++a)
423 {
424 VirtualSystemDescriptionType_T t = retTypes[a];
425
426 Utf8Str strOverride;
427
428 Bstr bstrFinalValue = aVBoxValues[a];
429
430 bool fIgnoreThis = mapIgnoresMapsPerVsys[i][a];
431
432 aEnabled[a] = true;
433
434 switch (t)
435 {
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);
447 break;
448
449 case VirtualSystemDescriptionType_Name:
450 if (findArgValue(strOverride, pmapArgs, "vmname"))
451 {
452 bstrFinalValue = strOverride;
453 RTPrintf("%2u: VM name specified with --vmname: \"%ls\"\n",
454 a, bstrFinalValue.raw());
455 }
456 else
457 RTPrintf("%2u: Suggested VM name \"%ls\""
458 "\n (change with \"--vsys %u --vmname <name>\")\n",
459 a, bstrFinalValue.raw(), i);
460 break;
461
462 case VirtualSystemDescriptionType_Product:
463 RTPrintf("%2u: Product (ignored): %ls\n",
464 a, aVBoxValues[a]);
465 break;
466
467 case VirtualSystemDescriptionType_ProductUrl:
468 RTPrintf("%2u: ProductUrl (ignored): %ls\n",
469 a, aVBoxValues[a]);
470 break;
471
472 case VirtualSystemDescriptionType_Vendor:
473 RTPrintf("%2u: Vendor (ignored): %ls\n",
474 a, aVBoxValues[a]);
475 break;
476
477 case VirtualSystemDescriptionType_VendorUrl:
478 RTPrintf("%2u: VendorUrl (ignored): %ls\n",
479 a, aVBoxValues[a]);
480 break;
481
482 case VirtualSystemDescriptionType_Version:
483 RTPrintf("%2u: Version (ignored): %ls\n",
484 a, aVBoxValues[a]);
485 break;
486
487 case VirtualSystemDescriptionType_Description:
488 if (findArgValue(strOverride, pmapArgs, "description"))
489 {
490 bstrFinalValue = strOverride;
491 RTPrintf("%2u: Description specified with --description: \"%ls\"\n",
492 a, bstrFinalValue.raw());
493 }
494 else
495 RTPrintf("%2u: Description \"%ls\""
496 "\n (change with \"--vsys %u --description <desc>\")\n",
497 a, bstrFinalValue.raw(), i);
498 break;
499
500 case VirtualSystemDescriptionType_License:
501 ++cLicensesInTheWay;
502 if (findArgValue(strOverride, pmapArgs, "eula"))
503 {
504 if (strOverride == "show")
505 {
506 RTPrintf("%2u: End-user license agreement"
507 "\n (accept with \"--vsys %u --eula accept\"):"
508 "\n\n%ls\n\n",
509 a, i, bstrFinalValue.raw());
510 }
511 else if (strOverride == "accept")
512 {
513 RTPrintf("%2u: End-user license agreement (accepted)\n",
514 a);
515 --cLicensesInTheWay;
516 }
517 else
518 return errorSyntax(USAGE_IMPORTAPPLIANCE,
519 "Argument to --eula must be either \"show\" or \"accept\".");
520 }
521 else
522 RTPrintf("%2u: End-user license agreement"
523 "\n (display with \"--vsys %u --eula show\";"
524 "\n accept with \"--vsys %u --eula accept\")\n",
525 a, i, i);
526 break;
527
528 case VirtualSystemDescriptionType_CPU:
529 if (findArgValue(strOverride, pmapArgs, "cpus"))
530 {
531 uint32_t cCPUs;
532 if ( strOverride.toInt(cCPUs) == VINF_SUCCESS
533 && cCPUs >= VMM_MIN_CPU_COUNT
534 && cCPUs <= VMM_MAX_CPU_COUNT
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,
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);
545 }
546 else
547 RTPrintf("%2u: Number of CPUs: %ls\n (change with \"--vsys %u --cpus <n>\")\n",
548 a, bstrFinalValue.raw(), i);
549 break;
550
551 case VirtualSystemDescriptionType_Memory:
552 {
553 if (findArgValue(strOverride, pmapArgs, "memory"))
554 {
555 uint32_t ulMemMB;
556 if (VINF_SUCCESS == strOverride.toInt(ulMemMB))
557 {
558 bstrFinalValue = strOverride;
559 RTPrintf("%2u: Guest memory specified with --memory: %ls MB\n",
560 a, bstrFinalValue.raw());
561 }
562 else
563 return errorSyntax(USAGE_IMPORTAPPLIANCE,
564 "Argument to --memory option must be a non-negative number.");
565 }
566 else
567 RTPrintf("%2u: Guest memory: %ls MB\n (change with \"--vsys %u --memory <MB>\")\n",
568 a, bstrFinalValue.raw(), i);
569 break;
570 }
571
572 case VirtualSystemDescriptionType_HardDiskControllerIDE:
573 if (fIgnoreThis)
574 {
575 RTPrintf("%2u: IDE controller, type %ls -- disabled\n",
576 a,
577 aVBoxValues[a]);
578 aEnabled[a] = false;
579 }
580 else
581 RTPrintf("%2u: IDE controller, type %ls"
582 "\n (disable with \"--vsys %u --unit %u --ignore\")\n",
583 a,
584 aVBoxValues[a],
585 i, a);
586 break;
587
588 case VirtualSystemDescriptionType_HardDiskControllerSATA:
589 if (fIgnoreThis)
590 {
591 RTPrintf("%2u: SATA controller, type %ls -- disabled\n",
592 a,
593 aVBoxValues[a]);
594 aEnabled[a] = false;
595 }
596 else
597 RTPrintf("%2u: SATA controller, type %ls"
598 "\n (disable with \"--vsys %u --unit %u --ignore\")\n",
599 a,
600 aVBoxValues[a],
601 i, a);
602 break;
603
604 case VirtualSystemDescriptionType_HardDiskControllerSAS:
605 if (fIgnoreThis)
606 {
607 RTPrintf("%2u: SAS controller, type %ls -- disabled\n",
608 a,
609 aVBoxValues[a]);
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,
616 aVBoxValues[a],
617 i, a);
618 break;
619
620 case VirtualSystemDescriptionType_HardDiskControllerSCSI:
621 if (fIgnoreThis)
622 {
623 RTPrintf("%2u: SCSI controller, type %ls -- disabled\n",
624 a,
625 aVBoxValues[a]);
626 aEnabled[a] = false;
627 }
628 else
629 {
630 Utf8StrFmt strTypeArg("scsitype%u", a);
631 if (findArgValue(strOverride, pmapArgs, strTypeArg))
632 {
633 bstrFinalValue = strOverride;
634 RTPrintf("%2u: SCSI controller, type set with --unit %u --scsitype: \"%ls\"\n",
635 a,
636 a,
637 bstrFinalValue.raw());
638 }
639 else
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",
643 a,
644 aVBoxValues[a],
645 i, a, i, a);
646 }
647 break;
648
649 case VirtualSystemDescriptionType_HardDiskImage:
650 if (fIgnoreThis)
651 {
652 RTPrintf("%2u: Hard disk image: source image=%ls -- disabled\n",
653 a,
654 aOvfValues[a]);
655 aEnabled[a] = false;
656 }
657 else
658 {
659 Utf8StrFmt strTypeArg("disk%u", a);
660 RTCList<ImportOptions_T> optionsList = options.toList();
661
662 bstrFinalValue = aVBoxValues[a];
663
664 if (findArgValue(strOverride, pmapArgs, strTypeArg))
665 {
666 if (!optionsList.contains(ImportOptions_ImportToVDI))
667 {
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)
672 {
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 }
681 }
682 bstrFinalValue = strOverride;
683 }
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
693 RTPrintf("%2u: Hard disk image: source image=%ls, target path=%ls, %ls\n",
694 a,
695 aOvfValues[a],
696 bstrFinalValue.raw(),
697 aExtraConfigValues[a]);
698 }
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
701 unit. The target disk path is more important I leave it for future
702 improvments. */
703 Utf8StrFmt strTypeArg("controller%u", a);
704 if (findArgValue(strOverride, pmapArgs, strTypeArg))
705 {
706 // strOverride now has the controller index as a number, but we
707 // need a "controller=X" format string
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],
714 aVBoxValues[a],
715 aExtraConfigValues[a]);
716 }
717#endif
718 else
719 {
720 strOverride = aVBoxValues[a];
721
722 /*
723 * Current solution isn't optimal.
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 */
735 if (optionsList.contains(ImportOptions_ImportToVDI))
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
751 for (unsigned j = 0; j < mediumFormats.size(); ++j)
752 {
753 com::SafeArray<DeviceType_T> deviceType;
754 ComPtr<IMediumFormat> mediumFormat = mediumFormats[j];
755 CHECK_ERROR(mediumFormat, COMGETTER(Name)(bstrFormatName.asOutParam()));
756 Utf8Str strFormatName = Utf8Str(bstrFormatName);
757
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 */
771 bool fReplace = true;
772
773 const char *pszExtension = RTPathSuffix(strOverride.c_str());
774 if (pszExtension)
775 pszExtension++;
776
777 for (unsigned j = 0; j < extensions.size(); ++j)
778 {
779 Bstr bstrExt(extensions[j]);
780 Utf8Str strExtension(bstrExt);
781 if(strExtension.compare(pszExtension, Utf8Str::CaseInsensitive) == 0)
782 {
783 fReplace = false;
784 break;
785 }
786 }
787
788 if (fReplace)
789 {
790 strOverride = strOverride.stripSuffix();
791 strOverride = strOverride.append(".").append("vdi");
792 }
793 }
794
795 bstrFinalValue = strOverride;
796
797 RTPrintf("%2u: Hard disk image: source image=%ls, target path=%ls, %ls"
798 "\n (change target path with \"--vsys %u --unit %u --disk path\";"
799 "\n disable with \"--vsys %u --unit %u --ignore\")\n",
800 a,
801 aOvfValues[a],
802 bstrFinalValue.raw(),
803 aExtraConfigValues[a],
804 i, a, i, a);
805 }
806 }
807 break;
808
809 case VirtualSystemDescriptionType_CDROM:
810 if (fIgnoreThis)
811 {
812 RTPrintf("%2u: CD-ROM -- disabled\n",
813 a);
814 aEnabled[a] = false;
815 }
816 else
817 RTPrintf("%2u: CD-ROM"
818 "\n (disable with \"--vsys %u --unit %u --ignore\")\n",
819 a, i, a);
820 break;
821
822 case VirtualSystemDescriptionType_Floppy:
823 if (fIgnoreThis)
824 {
825 RTPrintf("%2u: Floppy -- disabled\n",
826 a);
827 aEnabled[a] = false;
828 }
829 else
830 RTPrintf("%2u: Floppy"
831 "\n (disable with \"--vsys %u --unit %u --ignore\")\n",
832 a, i, a);
833 break;
834
835 case VirtualSystemDescriptionType_NetworkAdapter:
836 RTPrintf("%2u: Network adapter: orig %ls, config %ls, extra %ls\n", /// @todo implement once we have a plan for the back-end
837 a,
838 aOvfValues[a],
839 aVBoxValues[a],
840 aExtraConfigValues[a]);
841 break;
842
843 case VirtualSystemDescriptionType_USBController:
844 if (fIgnoreThis)
845 {
846 RTPrintf("%2u: USB controller -- disabled\n",
847 a);
848 aEnabled[a] = false;
849 }
850 else
851 RTPrintf("%2u: USB controller"
852 "\n (disable with \"--vsys %u --unit %u --ignore\")\n",
853 a, i, a);
854 break;
855
856 case VirtualSystemDescriptionType_SoundCard:
857 if (fIgnoreThis)
858 {
859 RTPrintf("%2u: Sound card \"%ls\" -- disabled\n",
860 a,
861 aOvfValues[a]);
862 aEnabled[a] = false;
863 }
864 else
865 RTPrintf("%2u: Sound card (appliance expects \"%ls\", can change on import)"
866 "\n (disable with \"--vsys %u --unit %u --ignore\")\n",
867 a,
868 aOvfValues[a],
869 i,
870 a);
871 break;
872
873 case VirtualSystemDescriptionType_SettingsFile:
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);
884 break;
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
912 case VirtualSystemDescriptionType_CloudInstanceShape:
913 case VirtualSystemDescriptionType_CloudDomain:
914 case VirtualSystemDescriptionType_CloudBootDiskSize:
915 case VirtualSystemDescriptionType_CloudBucket:
916 case VirtualSystemDescriptionType_CloudOCIVCN:
917 case VirtualSystemDescriptionType_CloudPublicIP:
918 case VirtualSystemDescriptionType_CloudProfileName:
919 case VirtualSystemDescriptionType_CloudOCISubnet:
920 case VirtualSystemDescriptionType_CloudKeepObject:
921 case VirtualSystemDescriptionType_CloudLaunchInstance:
922 case VirtualSystemDescriptionType_Miscellaneous:
923 /** @todo VirtualSystemDescriptionType_Miscellaneous? */
924 break;
925
926 case VirtualSystemDescriptionType_Ignore:
927#ifdef VBOX_WITH_XPCOM_CPP_ENUM_HACK
928 case VirtualSystemDescriptionType_32BitHack:
929#endif
930 break;
931 }
932
933 bstrFinalValue.detachTo(&aFinalValues[a]);
934 }
935
936 if (fExecute)
937 CHECK_ERROR_BREAK(aVirtualSystemDescriptions[i],
938 SetFinalValues(ComSafeArrayAsInParam(aEnabled),
939 ComSafeArrayAsInParam(aFinalValues),
940 ComSafeArrayAsInParam(aExtraConfigValues)));
941
942 } // for (unsigned i = 0; i < cVirtualSystemDescriptions; ++i)
943
944 if (cLicensesInTheWay == 1)
945 RTMsgError("Cannot import until the license agreement listed above is accepted.");
946 else if (cLicensesInTheWay > 1)
947 RTMsgError("Cannot import until the %c license agreements listed above are accepted.", cLicensesInTheWay);
948
949 if (!cLicensesInTheWay && fExecute)
950 {
951 // go!
952 ComPtr<IProgress> progress;
953 CHECK_ERROR_BREAK(pAppliance,
954 ImportMachines(ComSafeArrayAsInParam(options), progress.asOutParam()));
955
956 rc = showProgress(progress);
957 CHECK_PROGRESS_ERROR_RET(progress, ("Appliance import failed"), RTEXITCODE_FAILURE);
958
959 if (SUCCEEDED(rc))
960 RTPrintf("Successfully imported the appliance.\n");
961 }
962 } // end if (aVirtualSystemDescriptions.size() > 0)
963 } while (0);
964
965 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
966}
967
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 }
1005
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 },
1016 { "--opc10", 'c', RTGETOPT_REQ_NOTHING },
1017 { "--manifest", 'm', RTGETOPT_REQ_NOTHING }, // obsoleted by --options
1018 { "--iso", 'I', RTGETOPT_REQ_NOTHING }, // obsoleted by --options
1019 { "--vsys", 's', RTGETOPT_REQ_UINT32 },
1020 { "--vmname", 'V', RTGETOPT_REQ_STRING },
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 },
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 },
1041};
1042
1043enum
1044{
1045 NOT_SET, LOCAL, CLOUD
1046} exportType;
1047
1048RTEXITCODE handleExportAppliance(HandlerArg *a)
1049{
1050 HRESULT rc = S_OK;
1051
1052 Utf8Str strOutputFile;
1053 Utf8Str strOvfFormat("ovf-1.0"); // the default export version
1054 bool fManifest = false; // the default
1055 bool fCloud = false; // the default
1056 exportType = NOT_SET;
1057 bool fExportISOImages = false; // the default
1058 com::SafeArray<ExportOptions_T> options;
1059 std::list< ComPtr<IMachine> > llMachines;
1060
1061 uint32_t ulCurVsys = (uint32_t)-1;
1062 // for each --vsys X command, maintain a map of command line items
1063 ArgsMapsMap mapArgsMapsPerVsys;
1064 do
1065 {
1066 int c;
1067
1068 RTGETOPTUNION ValueUnion;
1069 RTGETOPTSTATE GetState;
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,
1072 RT_ELEMENTS(g_aExportOptions), 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
1073
1074 Utf8Str strProductUrl;
1075 while ((c = RTGetOpt(&GetState, &ValueUnion)))
1076 {
1077 switch (c)
1078 {
1079 case 'o': // --output
1080 if (strOutputFile.length())
1081 return errorSyntax(USAGE_EXPORTAPPLIANCE, "You can only specify --output once.");
1082 else
1083 strOutputFile = ValueUnion.psz;
1084 break;
1085
1086 case 'l': // --legacy09/--ovf09
1087 strOvfFormat = "ovf-0.9";
1088 break;
1089
1090 case '1': // --ovf10
1091 strOvfFormat = "ovf-1.0";
1092 break;
1093
1094 case '2': // --ovf20
1095 strOvfFormat = "ovf-2.0";
1096 break;
1097
1098 case 'c': // --opc
1099 strOvfFormat = "opc-1.0";
1100 break;
1101
1102 case 'I': // --iso
1103 fExportISOImages = true;
1104 break;
1105
1106 case 'm': // --manifest
1107 fManifest = true;
1108 break;
1109
1110 case 's': // --vsys
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
1119 ulCurVsys = ValueUnion.u32;
1120 break;
1121
1122 case 'V': // --vmname
1123 if (exportType == NOT_SET || ulCurVsys == (uint32_t)-1)
1124 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys or --cloud argument.",
1125 GetState.pDef->pszLong);
1126 mapArgsMapsPerVsys[ulCurVsys]["vmname"] = ValueUnion.psz;
1127 break;
1128
1129 case 'p': // --product
1130 if (exportType != LOCAL)
1131 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
1132 mapArgsMapsPerVsys[ulCurVsys]["product"] = ValueUnion.psz;
1133 break;
1134
1135 case 'P': // --producturl
1136 if (exportType != LOCAL)
1137 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
1138 mapArgsMapsPerVsys[ulCurVsys]["producturl"] = ValueUnion.psz;
1139 break;
1140
1141 case 'n': // --vendor
1142 if (exportType != LOCAL)
1143 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
1144 mapArgsMapsPerVsys[ulCurVsys]["vendor"] = ValueUnion.psz;
1145 break;
1146
1147 case 'N': // --vendorurl
1148 if (exportType != LOCAL)
1149 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
1150 mapArgsMapsPerVsys[ulCurVsys]["vendorurl"] = ValueUnion.psz;
1151 break;
1152
1153 case 'v': // --version
1154 if (exportType != LOCAL)
1155 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
1156 mapArgsMapsPerVsys[ulCurVsys]["version"] = ValueUnion.psz;
1157 break;
1158
1159 case 'd': // --description
1160 if (exportType != LOCAL)
1161 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
1162 mapArgsMapsPerVsys[ulCurVsys]["description"] = ValueUnion.psz;
1163 break;
1164
1165 case 'e': // --eula
1166 if (exportType != LOCAL)
1167 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
1168 mapArgsMapsPerVsys[ulCurVsys]["eula"] = ValueUnion.psz;
1169 break;
1170
1171 case 'E': // --eulafile
1172 if (exportType != LOCAL)
1173 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
1174 mapArgsMapsPerVsys[ulCurVsys]["eulafile"] = ValueUnion.psz;
1175 break;
1176
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
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
1269 case VINF_GETOPT_NOT_OPTION:
1270 {
1271 Utf8Str strMachine(ValueUnion.psz);
1272 // must be machine: try UUID or name
1273 ComPtr<IMachine> machine;
1274 CHECK_ERROR_BREAK(a->virtualBox, FindMachine(Bstr(strMachine).raw(),
1275 machine.asOutParam()));
1276 if (machine)
1277 llMachines.push_back(machine);
1278 break;
1279 }
1280
1281 default:
1282 if (c > 0)
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);
1291 else if (ValueUnion.pDef)
1292 return errorSyntax(USAGE_EXPORTAPPLIANCE, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
1293 else
1294 return errorSyntax(USAGE_EXPORTAPPLIANCE, "%Rrs", c);
1295 }
1296
1297 if (FAILED(rc))
1298 break;
1299 }
1300
1301 if (FAILED(rc))
1302 break;
1303
1304 if (llMachines.empty())
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.");
1308
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
1323 ComPtr<IAppliance> pAppliance;
1324 CHECK_ERROR_BREAK(a->virtualBox, CreateAppliance(pAppliance.asOutParam()));
1325
1326 char *pszAbsFilePath = 0;
1327 if (strOutputFile.startsWith("S3://", RTCString::CaseInsensitive) ||
1328 strOutputFile.startsWith("SunCloud://", RTCString::CaseInsensitive) ||
1329 strOutputFile.startsWith("webdav://", RTCString::CaseInsensitive) ||
1330 strOutputFile.startsWith("OCI://", RTCString::CaseInsensitive))
1331 pszAbsFilePath = RTStrDup(strOutputFile.c_str());
1332 else
1333 pszAbsFilePath = RTPathAbsDup(strOutputFile.c_str());
1334
1335 std::list< ComPtr<IMachine> >::iterator itM;
1336 uint32_t i=0;
1337 for (itM = llMachines.begin();
1338 itM != llMachines.end();
1339 ++itM, ++i)
1340 {
1341 ComPtr<IMachine> pMachine = *itM;
1342 ComPtr<IVirtualSystemDescription> pVSD;
1343 CHECK_ERROR_BREAK(pMachine, ExportTo(pAppliance, Bstr(pszAbsFilePath).raw(), pVSD.asOutParam()));
1344
1345 // Add additional info to the virtual system description if the user wants so
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 {
1357 if (itD->first == "vmname")
1358 {
1359 //remove default value if user has specified new name (default value is set in the ExportTo())
1360 pVSD->RemoveDescriptionByType(VirtualSystemDescriptionType_Name);
1361 pVSD->AddDescription(VirtualSystemDescriptionType_Name,
1362 Bstr(itD->second).raw(),
1363 Bstr(itD->second).raw());
1364 }
1365 else if (itD->first == "product")
1366 pVSD->AddDescription(VirtualSystemDescriptionType_Product,
1367 Bstr(itD->second).raw(),
1368 Bstr(itD->second).raw());
1369 else if (itD->first == "producturl")
1370 pVSD->AddDescription(VirtualSystemDescriptionType_ProductUrl,
1371 Bstr(itD->second).raw(),
1372 Bstr(itD->second).raw());
1373 else if (itD->first == "vendor")
1374 pVSD->AddDescription(VirtualSystemDescriptionType_Vendor,
1375 Bstr(itD->second).raw(),
1376 Bstr(itD->second).raw());
1377 else if (itD->first == "vendorurl")
1378 pVSD->AddDescription(VirtualSystemDescriptionType_VendorUrl,
1379 Bstr(itD->second).raw(),
1380 Bstr(itD->second).raw());
1381 else if (itD->first == "version")
1382 pVSD->AddDescription(VirtualSystemDescriptionType_Version,
1383 Bstr(itD->second).raw(),
1384 Bstr(itD->second).raw());
1385 else if (itD->first == "description")
1386 pVSD->AddDescription(VirtualSystemDescriptionType_Description,
1387 Bstr(itD->second).raw(),
1388 Bstr(itD->second).raw());
1389 else if (itD->first == "eula")
1390 pVSD->AddDescription(VirtualSystemDescriptionType_License,
1391 Bstr(itD->second).raw(),
1392 Bstr(itD->second).raw());
1393 else if (itD->first == "eulafile")
1394 {
1395 Utf8Str strContent;
1396 void *pvFile;
1397 size_t cbFile;
1398 int irc = RTFileReadAll(itD->second.c_str(), &pvFile, &cbFile);
1399 if (RT_SUCCESS(irc))
1400 {
1401 Bstr bstrContent((char*)pvFile, cbFile);
1402 pVSD->AddDescription(VirtualSystemDescriptionType_License,
1403 bstrContent.raw(),
1404 bstrContent.raw());
1405 RTFileReadAllFree(pvFile, cbFile);
1406 }
1407 else
1408 {
1409 RTMsgError("Cannot read license file \"%s\" which should be included in the virtual system %u.",
1410 itD->second.c_str(), i);
1411 return RTEXITCODE_FAILURE;
1412 }
1413 }
1414 /* add cloud export settings */
1415 else if (itD->first == "cloudshape")
1416 pVSD->AddDescription(VirtualSystemDescriptionType_CloudInstanceShape,
1417 Bstr(itD->second).raw(),
1418 Bstr(itD->second).raw());
1419 else if (itD->first == "clouddomain")
1420 pVSD->AddDescription(VirtualSystemDescriptionType_CloudDomain,
1421 Bstr(itD->second).raw(),
1422 Bstr(itD->second).raw());
1423 else if (itD->first == "clouddisksize")
1424 pVSD->AddDescription(VirtualSystemDescriptionType_CloudBootDiskSize,
1425 Bstr(itD->second).raw(),
1426 Bstr(itD->second).raw());
1427 else if (itD->first == "cloudbucket")
1428 pVSD->AddDescription(VirtualSystemDescriptionType_CloudBucket,
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")
1436 pVSD->AddDescription(VirtualSystemDescriptionType_CloudPublicIP,
1437 Bstr(itD->second).raw(),
1438 Bstr(itD->second).raw());
1439 else if (itD->first == "cloudprofile")
1440 pVSD->AddDescription(VirtualSystemDescriptionType_CloudProfileName,
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")
1448 pVSD->AddDescription(VirtualSystemDescriptionType_CloudKeepObject,
1449 Bstr(itD->second).raw(),
1450 Bstr(itD->second).raw());
1451 else if (itD->first == "cloudlaunchinstance")
1452 pVSD->AddDescription(VirtualSystemDescriptionType_CloudLaunchInstance,
1453 Bstr(itD->second).raw(),
1454 Bstr(itD->second).raw());
1455 }
1456 }
1457 }
1458
1459 if (FAILED(rc))
1460 break;
1461
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)
1479 {
1480 RTStrFree(pszAbsFilePath);
1481 return rcExit;
1482 }
1483
1484 bstrPassword = strPassword;
1485 bstrPassword.detachTo(&aPasswords[idxId]);
1486 }
1487
1488 CHECK_ERROR_BREAK(pAppliance, AddPasswords(ComSafeArrayAsInParam(aIdentifiers),
1489 ComSafeArrayAsInParam(aPasswords)));
1490 }
1491
1492 if (fManifest)
1493 options.push_back(ExportOptions_CreateManifest);
1494
1495 if (fExportISOImages)
1496 options.push_back(ExportOptions_ExportDVDImages);
1497
1498 ComPtr<IProgress> progress;
1499 CHECK_ERROR_BREAK(pAppliance, Write(Bstr(strOvfFormat).raw(),
1500 ComSafeArrayAsInParam(options),
1501 Bstr(pszAbsFilePath).raw(),
1502 progress.asOutParam()));
1503 RTStrFree(pszAbsFilePath);
1504
1505 rc = showProgress(progress);
1506 CHECK_PROGRESS_ERROR_RET(progress, ("Appliance write failed"), RTEXITCODE_FAILURE);
1507
1508 if (SUCCEEDED(rc))
1509 RTPrintf("Successfully exported %d machine(s).\n", llMachines.size());
1510
1511 } while (0);
1512
1513 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1514}
1515
1516#endif /* !VBOX_ONLY_DOCS */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use