VirtualBox

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

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

bugref:9555. Fixed error: enumeration value 'VirtualSystemDescriptionType_CloudBootVolumeId' not handled in switch.

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

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