VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxManage/VBoxManageImport.cpp@ 17867

Last change on this file since 17867 was 17827, checked in by vboxsync, 16 years ago

OVF: move warnings from IVirtualSystem to IAppliance; fix error messages when OVF wants more than one SCSI controller; API docs

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 26.1 KB
Line 
1/* $Id: VBoxManageImport.cpp 17827 2009-03-13 14:14:08Z vboxsync $ */
2/** @file
3 * VBoxManage - The appliance-related commands.
4 */
5
6/*
7 * Copyright (C) 2009 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22#ifndef VBOX_ONLY_DOCS
23
24/*******************************************************************************
25* Header Files *
26*******************************************************************************/
27#ifndef VBOX_ONLY_DOCS
28#include <VBox/com/com.h>
29#include <VBox/com/string.h>
30#include <VBox/com/Guid.h>
31#include <VBox/com/array.h>
32#include <VBox/com/ErrorInfo.h>
33#include <VBox/com/errorprint2.h>
34#include <VBox/com/EventQueue.h>
35
36#include <VBox/com/VirtualBox.h>
37
38#include <list>
39#include <map>
40#endif /* !VBOX_ONLY_DOCS */
41
42#include <iprt/stream.h>
43#include <iprt/getopt.h>
44
45#include <VBox/log.h>
46
47#include "VBoxManage.h"
48using namespace com;
49
50
51// funcs
52///////////////////////////////////////////////////////////////////////////////
53
54typedef std::map<Utf8Str, Utf8Str> ArgsMap; // pairs of strings like "-vmname" => "newvmname"
55typedef std::map<uint32_t, ArgsMap> ArgsMapsMap; // map of maps, one for each virtual system, sorted by index
56
57typedef std::map<uint32_t, bool> IgnoresMap; // pairs of numeric description entry indices
58typedef std::map<uint32_t, IgnoresMap> IgnoresMapsMap; // map of maps, one for each virtual system, sorted by index
59
60static bool findArgValue(Utf8Str &strOut,
61 ArgsMap *pmapArgs,
62 const Utf8Str &strKey)
63{
64 if (pmapArgs)
65 {
66 ArgsMap::iterator it;
67 it = pmapArgs->find(strKey);
68 if (it != pmapArgs->end())
69 {
70 strOut = it->second;
71 pmapArgs->erase(it);
72 return true;
73 }
74 }
75
76 return false;
77}
78
79int handleImportAppliance(HandlerArg *a)
80{
81 HRESULT rc = S_OK;
82
83 Utf8Str strOvfFilename;
84 bool fExecute = true; // if true, then we actually do the import
85
86 uint32_t ulCurVsys = (uint32_t)-1;
87
88 // for each -vsys X command, maintain a map of command line items
89 // (we'll parse them later after interpreting the OVF, when we can
90 // actually check whether they make sense semantically)
91 ArgsMapsMap mapArgsMapsPerVsys;
92 IgnoresMapsMap mapIgnoresMapsPerVsys;
93
94 for (int i = 0;
95 i < a->argc;
96 ++i)
97 {
98 bool fIsIgnore = false;
99 Utf8Str strThisArg(a->argv[i]);
100 if ( (strThisArg == "--dry-run")
101 || (strThisArg == "-dry-run")
102 || (strThisArg == "-n")
103 )
104 fExecute = false;
105 else if (strThisArg == "-vsys")
106 {
107 if (++i < a->argc)
108 {
109 uint32_t ulVsys;
110 if (VINF_SUCCESS != (rc = Utf8Str(a->argv[i]).toInt(ulVsys))) // don't use SUCCESS() macro, fail even on warnings
111 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Argument to -vsys option must be a non-negative number.");
112
113 ulCurVsys = ulVsys;
114 }
115 else
116 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Missing argument to -vsys option.");
117 }
118 else if ( (strThisArg == "-ostype")
119 || (strThisArg == "-vmname")
120 || (strThisArg == "-memory")
121 || (fIsIgnore = (strThisArg == "-ignore"))
122 || (strThisArg.substr(0, 5) == "-type")
123 || (strThisArg.substr(0, 11) == "-controller")
124 )
125 {
126 if (ulCurVsys == (uint32_t)-1)
127 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding -vsys argument.", strThisArg.c_str());
128
129 if (++i < a->argc)
130 if (fIsIgnore)
131 {
132 uint32_t ulItem;
133 if (VINF_SUCCESS != Utf8Str(a->argv[i]).toInt(ulItem))
134 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Argument to -vsys option must be a non-negative number.");
135
136 mapIgnoresMapsPerVsys[ulCurVsys][ulItem] = true;
137 }
138 else
139 {
140 // store both this arg and the next one in the strings map for later parsing
141 mapArgsMapsPerVsys[ulCurVsys][strThisArg] = Utf8Str(a->argv[i]);
142 }
143 else
144 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Missing argument to \"%s\" option.", strThisArg.c_str());
145 }
146 else if (strThisArg[0] == '-')
147 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Unknown option \"%s\".", strThisArg.c_str());
148 else if (!strOvfFilename)
149 strOvfFilename = strThisArg;
150 else
151 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Too many arguments for \"import\" command.");
152 }
153
154 if (!strOvfFilename)
155 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Not enough arguments for \"import\" command.");
156
157 do
158 {
159 Bstr bstrOvfFilename(strOvfFilename);
160 ComPtr<IAppliance> pAppliance;
161 CHECK_ERROR_BREAK(a->virtualBox, CreateAppliance(pAppliance.asOutParam()));
162
163 CHECK_ERROR_BREAK(pAppliance, Read(bstrOvfFilename));
164
165 // call interpret(); this can yield both warnings and errors, so we need
166 // to tinker with the error info a bit
167 RTPrintf("Interpreting %s...\n", strOvfFilename.c_str());
168 rc = pAppliance->Interpret();
169 com::ErrorInfo info0(pAppliance);
170
171 com::SafeArray<BSTR> aWarnings;
172 if (SUCCEEDED(pAppliance->GetWarnings(ComSafeArrayAsOutParam(aWarnings))))
173 {
174 unsigned cWarnings = aWarnings.size();
175 for (unsigned i = 0; i < cWarnings; ++i)
176 {
177 Bstr bstrWarning(aWarnings[i]);
178 RTPrintf("WARNING: %ls.\n", bstrWarning.raw());
179 }
180 }
181
182 if (FAILED(rc)) // during interpret, after printing warnings
183 {
184 com::GluePrintErrorInfo(info0);
185 com::GluePrintErrorContext("Interpret", __FILE__, __LINE__);
186 break;
187 }
188
189 RTPrintf("OK.\n");
190
191 // fetch all disks
192 com::SafeArray<BSTR> retDisks;
193 CHECK_ERROR_BREAK(pAppliance,
194 COMGETTER(Disks)(ComSafeArrayAsOutParam(retDisks)));
195 if (retDisks.size() > 0)
196 {
197 RTPrintf("Disks:");
198 for (unsigned i = 0; i < retDisks.size(); i++)
199 RTPrintf(" %ls", retDisks[i]);
200 RTPrintf("\n");
201 }
202
203 // fetch virtual system descriptions
204 com::SafeIfaceArray<IVirtualSystemDescription> aVirtualSystemDescriptions;
205 CHECK_ERROR_BREAK(pAppliance,
206 COMGETTER(VirtualSystemDescriptions)(ComSafeArrayAsOutParam(aVirtualSystemDescriptions)));
207
208 uint32_t cVirtualSystemDescriptions = aVirtualSystemDescriptions.size();
209
210 // match command line arguments with virtual system descriptions;
211 // this is only to sort out invalid indices at this time
212 ArgsMapsMap::const_iterator it;
213 for (it = mapArgsMapsPerVsys.begin();
214 it != mapArgsMapsPerVsys.end();
215 ++it)
216 {
217 uint32_t ulVsys = it->first;
218 if (ulVsys >= cVirtualSystemDescriptions)
219 return errorSyntax(USAGE_IMPORTAPPLIANCE,
220 "Invalid index %RI32 with -vsys option; the OVF contains only %RI32 virtual system(s).",
221 ulVsys, cVirtualSystemDescriptions);
222 }
223
224 // dump virtual system descriptions and match command-line arguments
225 if (cVirtualSystemDescriptions > 0)
226 {
227 for (unsigned i = 0; i < cVirtualSystemDescriptions; ++i)
228 {
229 com::SafeArray<VirtualSystemDescriptionType_T> retTypes;
230 com::SafeArray<BSTR> aRefs;
231 com::SafeArray<BSTR> aOvfValues;
232 com::SafeArray<BSTR> aVboxValues;
233 com::SafeArray<BSTR> aExtraConfigValues;
234 CHECK_ERROR_BREAK(aVirtualSystemDescriptions[i],
235 GetDescription(ComSafeArrayAsOutParam(retTypes),
236 ComSafeArrayAsOutParam(aRefs),
237 ComSafeArrayAsOutParam(aOvfValues),
238 ComSafeArrayAsOutParam(aVboxValues),
239 ComSafeArrayAsOutParam(aExtraConfigValues)));
240
241 RTPrintf("Virtual system %i:\n", i);
242
243 // look up the corresponding command line options, if any
244 ArgsMap *pmapArgs = NULL;
245 ArgsMapsMap::iterator itm = mapArgsMapsPerVsys.find(i);
246 if (itm != mapArgsMapsPerVsys.end())
247 pmapArgs = &itm->second;
248
249 // this collects the final values for setFinalValues()
250 com::SafeArray<BOOL> aEnabled(retTypes.size());
251 com::SafeArray<BSTR> aFinalValues(retTypes.size());
252
253 for (unsigned a = 0; a < retTypes.size(); ++a)
254 {
255 VirtualSystemDescriptionType_T t = retTypes[a];
256
257 Utf8Str strOverride;
258
259 Bstr bstrFinalValue = aVboxValues[a];
260
261 bool fIgnoreThis = mapIgnoresMapsPerVsys[i][a];
262
263 aEnabled[a] = true;
264
265 switch (t)
266 {
267 case VirtualSystemDescriptionType_Name:
268 if (findArgValue(strOverride, pmapArgs, "-vmname"))
269 {
270 bstrFinalValue = strOverride;
271 RTPrintf("%2d: VM name specified with -vmname: \"%ls\"\n",
272 a, bstrFinalValue.raw());
273 }
274 else
275 RTPrintf("%2d: Suggested VM name \"%ls\""
276 "\n (change with \"-vsys %d -vmname <name>\")\n",
277 a, bstrFinalValue.raw(), i);
278 break;
279
280 case VirtualSystemDescriptionType_OS:
281 if (findArgValue(strOverride, pmapArgs, "-ostype"))
282 {
283 bstrFinalValue = strOverride;
284 RTPrintf("%2d: OS type specified with -ostype: \"%ls\"\n",
285 a, bstrFinalValue.raw());
286 }
287 else
288 RTPrintf("%2d: Suggested OS type: \"%ls\""
289 "\n (change with \"-vsys %d -ostype <type>\"; use \"list ostypes\" to list all)\n",
290 a, bstrFinalValue.raw(), i);
291 break;
292
293 case VirtualSystemDescriptionType_CPU:
294 RTPrintf("%2d: Number of CPUs (ignored): %ls\n",
295 a, aVboxValues[a]);
296 break;
297
298 case VirtualSystemDescriptionType_Memory:
299 {
300 if (findArgValue(strOverride, pmapArgs, "-memory"))
301 {
302 uint32_t ulMemMB;
303 if (VINF_SUCCESS == strOverride.toInt(ulMemMB))
304 {
305 bstrFinalValue = strOverride;
306 RTPrintf("%2d: Guest memory specified with -memory: %ls MB\n",
307 a, bstrFinalValue.raw());
308 }
309 else
310 return errorSyntax(USAGE_IMPORTAPPLIANCE,
311 "Argument to -memory option must be a non-negative number.");
312 }
313 else
314 RTPrintf("%2d: Guest memory: %ls MB\n (change with \"-vsys %d -memory <MB>\")\n",
315 a, bstrFinalValue.raw(), i);
316 }
317 break;
318
319 case VirtualSystemDescriptionType_HardDiskControllerIDE:
320 if (fIgnoreThis)
321 {
322 RTPrintf("%2d: IDE controller, type %ls -- disabled\n",
323 a,
324 aVboxValues[a]);
325 aEnabled[a] = false;
326 }
327 else
328 RTPrintf("%2d: IDE controller, type %ls"
329 "\n (disable with \"-vsys %d -ignore %d\")\n",
330 a,
331 aVboxValues[a],
332 i, a);
333 break;
334
335 case VirtualSystemDescriptionType_HardDiskControllerSATA:
336 if (fIgnoreThis)
337 {
338 RTPrintf("%2d: SATA controller, type %ls -- disabled\n",
339 a,
340 aVboxValues[a]);
341 aEnabled[a] = false;
342 }
343 else
344 RTPrintf("%2d: SATA controller, type %ls"
345 "\n (disable with \"-vsys %d -ignore %d\")\n",
346 a,
347 aVboxValues[a],
348 i, a);
349 break;
350
351 case VirtualSystemDescriptionType_HardDiskControllerSCSI:
352 if (fIgnoreThis)
353 {
354 RTPrintf("%2d: SCSI controller, type %ls -- disabled\n",
355 a,
356 aVboxValues[a]);
357 aEnabled[a] = false;
358 }
359 else
360 {
361 Utf8StrFmt strTypeArg("-type%RI16", a);
362 if (findArgValue(strOverride, pmapArgs, strTypeArg))
363 {
364 bstrFinalValue = strOverride;
365 RTPrintf("%2d: SCSI controller, type set with -type%d: %ls\n",
366 a,
367 a,
368 bstrFinalValue.raw());
369 }
370 else
371 RTPrintf("%2d: SCSI controller, type %ls"
372 "\n (change with \"-vsys %d -type%d {BusLogic|LsiLogic}\";"
373 "\n disable with \"-vsys %d -ignore %d\")\n",
374 a,
375 aVboxValues[a],
376 i, a, i, a);
377 }
378 break;
379
380 case VirtualSystemDescriptionType_HardDiskImage:
381 if (fIgnoreThis)
382 {
383 RTPrintf("%2d: Hard disk image: source image=%ls -- disabled\n",
384 a,
385 aOvfValues[a]);
386 aEnabled[a] = false;
387 }
388 else
389 {
390 Utf8StrFmt strTypeArg("-controller%RI16", a);
391 if (findArgValue(strOverride, pmapArgs, strTypeArg))
392 {
393 // strOverride now has the controller index as a number, but we
394 // need a "controller=X" format string
395 strOverride = Utf8StrFmt("controller=%s", strOverride.c_str());
396 Bstr bstrExtraConfigValue = strOverride;
397 bstrExtraConfigValue.detachTo(&aExtraConfigValues[a]);
398 RTPrintf("%2d: Hard disk image: source image=%ls, target path=%ls, %ls\n",
399 a,
400 aOvfValues[a],
401 aVboxValues[a],
402 aExtraConfigValues[a]);
403 }
404 else
405 RTPrintf("%2d: Hard disk image: source image=%ls, target path=%ls, %ls"
406 "\n (change controller with \"-vsys %d -controller%d <id>\";"
407 "\n disable with \"-vsys %d -ignore %d\")\n",
408 a,
409 aOvfValues[a],
410 aVboxValues[a],
411 aExtraConfigValues[a],
412 i, a, i, a);
413 }
414 break;
415
416 case VirtualSystemDescriptionType_CDROM:
417 if (fIgnoreThis)
418 {
419 RTPrintf("%2d: CD-ROM -- disabled\n",
420 a);
421 aEnabled[a] = false;
422 }
423 else
424 RTPrintf("%2d: CD-ROM"
425 "\n (disable with \"-vsys %d -ignore %d\")\n",
426 a, i, a);
427 break;
428
429 case VirtualSystemDescriptionType_Floppy:
430 if (fIgnoreThis)
431 {
432 RTPrintf("%2d: Floppy -- disabled\n",
433 a);
434 aEnabled[a] = false;
435 }
436 else
437 RTPrintf("%2d: Floppy"
438 "\n (disable with \"-vsys %d -ignore %d\")\n",
439 a, i, a);
440 break;
441
442 case VirtualSystemDescriptionType_NetworkAdapter:
443 RTPrintf("%2d: Network adapter: orig %ls, config %ls, extra %ls\n", // @todo implement once we have a plan for the back-end
444 a,
445 aOvfValues[a],
446 aVboxValues[a],
447 aExtraConfigValues[a]);
448 break;
449
450 case VirtualSystemDescriptionType_USBController:
451 if (fIgnoreThis)
452 {
453 RTPrintf("%2d: USB controller -- disabled\n",
454 a);
455 aEnabled[a] = false;
456 }
457 else
458 RTPrintf("%2d: USB controller"
459 "\n (disable with \"-vsys %d -ignore %d\")\n",
460 a, i, a);
461 break;
462
463 case VirtualSystemDescriptionType_SoundCard:
464 if (fIgnoreThis)
465 {
466 RTPrintf("%2d: Sound card \"%ls\" -- disabled\n",
467 a,
468 aOvfValues[a]);
469 aEnabled[a] = false;
470 }
471 else
472 RTPrintf("%2d: Sound card (appliance expects \"%ls\", can change on import)"
473 "\n (disable with \"-vsys %d -ignore %d\")\n",
474 a,
475 aOvfValues[a],
476 i,
477 a);
478 break;
479 }
480
481 bstrFinalValue.detachTo(&aFinalValues[a]);
482 }
483
484 if (fExecute)
485 CHECK_ERROR_BREAK(aVirtualSystemDescriptions[i],
486 SetFinalValues(ComSafeArrayAsInParam(aEnabled),
487 ComSafeArrayAsInParam(aFinalValues),
488 ComSafeArrayAsInParam(aExtraConfigValues)));
489
490 } // for (unsigned i = 0; i < cVirtualSystemDescriptions; ++i)
491
492 if (fExecute)
493 {
494 // go!
495 ComPtr<IProgress> progress;
496 CHECK_ERROR_BREAK(pAppliance,
497 ImportMachines(progress.asOutParam()));
498
499 showProgress(progress);
500
501 if (SUCCEEDED(rc))
502 progress->COMGETTER(ResultCode)(&rc);
503
504 if (FAILED(rc))
505 {
506 com::ProgressErrorInfo info(progress);
507 com::GluePrintErrorInfo(info);
508 com::GluePrintErrorContext("ImportAppliance", __FILE__, __LINE__);
509 }
510 else
511 RTPrintf("Successfully imported the appliance.\n");
512 }
513 } // end if (aVirtualSystemDescriptions.size() > 0)
514 } while (0);
515
516 return SUCCEEDED(rc) ? 0 : 1;
517}
518
519static const RTGETOPTDEF g_aExportOptions[]
520 = {
521 { "--output", 'o', RTGETOPT_REQ_STRING },
522 };
523
524int handleExportAppliance(HandlerArg *a)
525{
526 HRESULT rc = S_OK;
527
528 Utf8Str strOutputFile;
529 std::list< ComPtr<IMachine> > llMachines;
530
531 do
532 {
533 int c;
534
535 RTGETOPTUNION ValueUnion;
536 RTGETOPTSTATE GetState;
537 RTGetOptInit(&GetState,
538 a->argc,
539 a->argv,
540 g_aExportOptions,
541 RT_ELEMENTS(g_aExportOptions),
542 0, // start at 0 even though arg 1 was "list" because main() has hacked both the argc and argv given to us
543 0 /* fFlags */);
544 while ((c = RTGetOpt(&GetState, &ValueUnion)))
545 {
546 switch (c)
547 {
548 case 'o': // --output
549 if (strOutputFile.length())
550 return errorSyntax(USAGE_EXPORTAPPLIANCE, "You can only specify --output once.");
551 else
552 strOutputFile = ValueUnion.psz;
553 break;
554
555 case VINF_GETOPT_NOT_OPTION:
556 {
557 Utf8Str strMachine(ValueUnion.psz);
558 // must be machine: try UUID or name
559 ComPtr<IMachine> machine;
560 /* assume it's a UUID */
561 rc = a->virtualBox->GetMachine(Guid(strMachine), machine.asOutParam());
562 if (FAILED(rc) || !machine)
563 {
564 /* must be a name */
565 CHECK_ERROR_BREAK(a->virtualBox, FindMachine(Bstr(strMachine), machine.asOutParam()));
566 }
567
568 if (machine)
569 llMachines.push_back(machine);
570 }
571 break;
572
573 default:
574 if (c > 0)
575 return errorSyntax(USAGE_LIST, "missing case: %c\n", c);
576 else if (ValueUnion.pDef)
577 return errorSyntax(USAGE_LIST, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
578 else
579 return errorSyntax(USAGE_LIST, "%Rrs", c);
580 }
581
582 if (FAILED(rc))
583 break;
584 }
585
586 if (FAILED(rc))
587 break;
588
589 if (llMachines.size() == 0)
590 return errorSyntax(USAGE_EXPORTAPPLIANCE, "At least one machine must be specified with the export command.");
591 if (!strOutputFile.length())
592 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Missing --output argument with export command.");
593
594 ComPtr<IAppliance> pAppliance;
595 CHECK_ERROR_BREAK(a->virtualBox, CreateAppliance(pAppliance.asOutParam()));
596
597 std::list< ComPtr<IMachine> >::iterator itM;
598 for (itM = llMachines.begin();
599 itM != llMachines.end();
600 ++itM)
601 {
602 ComPtr<IMachine> pMachine = *itM;
603 CHECK_ERROR_BREAK(pMachine, Export(pAppliance));
604 }
605
606 if (FAILED(rc))
607 break;
608
609 ComPtr<IProgress> progress;
610 CHECK_ERROR_BREAK(pAppliance, Write(Bstr(strOutputFile), progress.asOutParam()));
611
612 showProgress(progress);
613
614 if (SUCCEEDED(rc))
615 progress->COMGETTER(ResultCode)(&rc);
616
617 if (FAILED(rc))
618 {
619 com::ProgressErrorInfo info(progress);
620 com::GluePrintErrorInfo(info);
621 com::GluePrintErrorContext("Write", __FILE__, __LINE__);
622 }
623 else
624 RTPrintf("Successfully exported %d machine(s).\n", llMachines.size());
625
626 } while (0);
627
628 return SUCCEEDED(rc) ? 0 : 1;
629}
630
631#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