VirtualBox

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

Last change on this file since 81893 was 81517, checked in by vboxsync, 5 years ago

Improved the action (local or cloud) check for export operation.

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