VirtualBox

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

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

include/VBox/com/Guid.h: Don't include iprt/err.h for no good reason. bugref:9344

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