VirtualBox

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

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

bugref:9416. The part of OCI import logic. New functions - OciRestClient::exportImage, createImageFromInstance; new API functions - ICloudClient::importInstance, getInstanceInfo; new helper classes - HandleRestBinaryResponse, DownloadSession; changes in the Appliance import and export parts.

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