VirtualBox

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

Last change on this file since 81510 was 81400, checked in by vboxsync, 5 years ago

:fix warning because our cc1plus says 'all warnings being treated as errors'.

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

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