VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxManage/VBoxManageCloud.cpp@ 93507

Last change on this file since 93507 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 107.8 KB
Line 
1/* $Id: VBoxManageCloud.cpp 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 * VBoxManageCloud - The cloud related commands.
4 */
5
6/*
7 * Copyright (C) 2006-2022 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#include <VBox/com/com.h>
19#include <VBox/com/string.h>
20#include <VBox/com/Guid.h>
21#include <VBox/com/array.h>
22#include <VBox/com/ErrorInfo.h>
23#include <VBox/com/errorprint.h>
24#include <VBox/com/VirtualBox.h>
25
26#include <iprt/ctype.h>
27#include <iprt/getopt.h>
28#include <iprt/stream.h>
29#include <iprt/string.h>
30#include <iprt/thread.h>
31#include <iprt/uuid.h>
32#include <iprt/file.h>
33#include <iprt/http.h>
34#include <VBox/log.h>
35
36#include <iprt/cpp/path.h>
37
38#include "VBoxManage.h"
39
40#include <list>
41
42using namespace com;//at least for Bstr
43
44DECLARE_TRANSLATION_CONTEXT(Cloud);
45
46
47/**
48 * Common Cloud options.
49 */
50typedef struct
51{
52 struct {
53 const char *pszProviderName;
54 ComPtr<ICloudProvider> pCloudProvider;
55 }provider;
56 struct {
57 const char *pszProfileName;
58 ComPtr<ICloudProfile> pCloudProfile;
59 }profile;
60
61} CLOUDCOMMONOPT;
62typedef CLOUDCOMMONOPT *PCLOUDCOMMONOPT;
63
64static HRESULT checkAndSetCommonOptions(HandlerArg *a, PCLOUDCOMMONOPT pCommonOpts)
65{
66 HRESULT hrc = S_OK;
67
68 Bstr bstrProvider(pCommonOpts->provider.pszProviderName);
69 Bstr bstrProfile(pCommonOpts->profile.pszProfileName);
70
71 /* check for required options */
72 if (bstrProvider.isEmpty())
73 {
74 errorSyntax(USAGE_S_NEWCMD, Cloud::tr("Parameter --provider is required"));
75 return E_FAIL;
76 }
77 if (bstrProfile.isEmpty())
78 {
79 errorSyntax(USAGE_S_NEWCMD, Cloud::tr("Parameter --profile is required"));
80 return E_FAIL;
81 }
82
83 ComPtr<IVirtualBox> pVirtualBox = a->virtualBox;
84 ComPtr<ICloudProviderManager> pCloudProviderManager;
85 CHECK_ERROR2_RET(hrc, pVirtualBox,
86 COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam()),
87 RTEXITCODE_FAILURE);
88
89 ComPtr<ICloudProvider> pCloudProvider;
90 CHECK_ERROR2_RET(hrc, pCloudProviderManager,
91 GetProviderByShortName(bstrProvider.raw(), pCloudProvider.asOutParam()),
92 RTEXITCODE_FAILURE);
93 pCommonOpts->provider.pCloudProvider = pCloudProvider;
94
95 ComPtr<ICloudProfile> pCloudProfile;
96 CHECK_ERROR2_RET(hrc, pCloudProvider,
97 GetProfileByName(bstrProfile.raw(), pCloudProfile.asOutParam()),
98 RTEXITCODE_FAILURE);
99 pCommonOpts->profile.pCloudProfile = pCloudProfile;
100
101 return hrc;
102}
103
104
105/**
106 * List all available cloud instances for the specified cloud provider.
107 * Available cloud instance is one which state whether "running" or "stopped".
108 *
109 * @returns RTEXITCODE
110 * @param a is the list of passed arguments
111 * @param iFirst is the position of the first unparsed argument in the arguments list
112 * @param pCommonOpts is a pointer to the structure CLOUDCOMMONOPT with some common
113 * arguments which have been already parsed before
114 */
115static RTEXITCODE listCloudInstances(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts)
116{
117 static const RTGETOPTDEF s_aOptions[] =
118 {
119 { "--compartment-id", 'c', RTGETOPT_REQ_STRING },
120 { "--state", 's', RTGETOPT_REQ_STRING },
121 { "help", 1001, RTGETOPT_REQ_NOTHING },
122 { "--help", 1002, RTGETOPT_REQ_NOTHING }
123 };
124 RTGETOPTSTATE GetState;
125 RTGETOPTUNION ValueUnion;
126 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
127 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
128
129 Utf8Str strCompartmentId;
130 com::SafeArray<CloudMachineState_T> machineStates;
131
132 int c;
133 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
134 {
135 switch (c)
136 {
137 case 'c':
138 strCompartmentId = ValueUnion.psz;
139 break;
140
141 case 's':
142 {
143 const char * const pszState = ValueUnion.psz;
144
145 if (RTStrICmp(pszState, "creatingimage") == 0)
146 machineStates.push_back(CloudMachineState_CreatingImage);
147 else if (RTStrICmp(pszState, "paused") == 0) /* XXX */
148 machineStates.push_back(CloudMachineState_Stopped);
149 else if (RTStrICmp(pszState, "provisioning") == 0)
150 machineStates.push_back(CloudMachineState_Provisioning);
151 else if (RTStrICmp(pszState, "running") == 0)
152 machineStates.push_back(CloudMachineState_Running);
153 else if (RTStrICmp(pszState, "starting") == 0)
154 machineStates.push_back(CloudMachineState_Starting);
155 else if (RTStrICmp(pszState, "stopped") == 0)
156 machineStates.push_back(CloudMachineState_Stopped);
157 else if (RTStrICmp(pszState, "stopping") == 0)
158 machineStates.push_back(CloudMachineState_Stopping);
159 else if (RTStrICmp(pszState, "terminated") == 0)
160 machineStates.push_back(CloudMachineState_Terminated);
161 else if (RTStrICmp(pszState, "terminating") == 0)
162 machineStates.push_back(CloudMachineState_Terminating);
163 else
164 return errorArgument(Cloud::tr("Unknown cloud instance state \"%s\""), pszState);
165 break;
166 }
167 case 1001:
168 case 1002:
169 printHelp(g_pStdOut);
170 return RTEXITCODE_SUCCESS;
171 case VINF_GETOPT_NOT_OPTION:
172 return errorUnknownSubcommand(ValueUnion.psz);
173
174 default:
175 return errorGetOpt(c, &ValueUnion);
176 }
177 }
178
179 HRESULT hrc = S_OK;
180
181 /* Delayed check. It allows us to print help information.*/
182 hrc = checkAndSetCommonOptions(a, pCommonOpts);
183 if (FAILED(hrc))
184 return RTEXITCODE_FAILURE;
185
186 ComPtr<IVirtualBox> pVirtualBox = a->virtualBox;
187
188 ComPtr<ICloudProviderManager> pCloudProviderManager;
189 CHECK_ERROR2_RET(hrc, pVirtualBox,
190 COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam()),
191 RTEXITCODE_FAILURE);
192
193 ComPtr<ICloudProvider> pCloudProvider;
194 CHECK_ERROR2_RET(hrc, pCloudProviderManager,
195 GetProviderByShortName(Bstr(pCommonOpts->provider.pszProviderName).raw(), pCloudProvider.asOutParam()),
196 RTEXITCODE_FAILURE);
197
198 ComPtr<ICloudProfile> pCloudProfile;
199 CHECK_ERROR2_RET(hrc, pCloudProvider,
200 GetProfileByName(Bstr(pCommonOpts->profile.pszProfileName).raw(), pCloudProfile.asOutParam()),
201 RTEXITCODE_FAILURE);
202
203 if (strCompartmentId.isNotEmpty())
204 {
205 CHECK_ERROR2_RET(hrc, pCloudProfile,
206 SetProperty(Bstr("compartment").raw(), Bstr(strCompartmentId).raw()),
207 RTEXITCODE_FAILURE);
208 }
209 else
210 {
211 RTPrintf(Cloud::tr("Parameter \'compartment\' is empty or absent.\n"
212 "Trying to get the compartment from the passed cloud profile \'%s\'\n"),
213 pCommonOpts->profile.pszProfileName);
214 Bstr bStrCompartmentId;
215 CHECK_ERROR2_RET(hrc, pCloudProfile,
216 GetProperty(Bstr("compartment").raw(), bStrCompartmentId.asOutParam()),
217 RTEXITCODE_FAILURE);
218 strCompartmentId = bStrCompartmentId;
219 if (strCompartmentId.isNotEmpty())
220 RTPrintf(Cloud::tr("Found the compartment \'%s\':\n"), strCompartmentId.c_str());
221 else
222 return errorSyntax(USAGE_S_NEWCMD, Cloud::tr("Parameter --compartment-id is required"));
223 }
224
225 Bstr bstrProfileName;
226 pCloudProfile->COMGETTER(Name)(bstrProfileName.asOutParam());
227
228 ComObjPtr<ICloudClient> oCloudClient;
229 CHECK_ERROR2_RET(hrc, pCloudProfile,
230 CreateCloudClient(oCloudClient.asOutParam()),
231 RTEXITCODE_FAILURE);
232
233 ComPtr<IStringArray> pVMNamesHolder;
234 ComPtr<IStringArray> pVMIdsHolder;
235 com::SafeArray<BSTR> arrayVMNames;
236 com::SafeArray<BSTR> arrayVMIds;
237 ComPtr<IProgress> pProgress;
238
239 RTPrintf(Cloud::tr("Reply is in the form \'instance name\' = \'instance id\'\n"));
240
241 CHECK_ERROR2_RET(hrc, oCloudClient,
242 ListInstances(ComSafeArrayAsInParam(machineStates),
243 pVMNamesHolder.asOutParam(),
244 pVMIdsHolder.asOutParam(),
245 pProgress.asOutParam()),
246 RTEXITCODE_FAILURE);
247 showProgress(pProgress);
248 CHECK_PROGRESS_ERROR_RET(pProgress, (Cloud::tr("Failed to list instances")), RTEXITCODE_FAILURE);
249
250 CHECK_ERROR2_RET(hrc,
251 pVMNamesHolder, COMGETTER(Values)(ComSafeArrayAsOutParam(arrayVMNames)),
252 RTEXITCODE_FAILURE);
253 CHECK_ERROR2_RET(hrc,
254 pVMIdsHolder, COMGETTER(Values)(ComSafeArrayAsOutParam(arrayVMIds)),
255 RTEXITCODE_FAILURE);
256
257 RTPrintf(Cloud::tr("The list of the instances for the cloud profile \'%ls\'\nand compartment \'%s\':\n"),
258 bstrProfileName.raw(), strCompartmentId.c_str());
259 size_t cIds = arrayVMIds.size();
260 size_t cNames = arrayVMNames.size();
261 for (size_t k = 0; k < cNames; k++)
262 {
263 Bstr value;
264 if (k < cIds)
265 value = arrayVMIds[k];
266 RTPrintf("\t%ls = %ls\n", arrayVMNames[k], value.raw());
267 }
268
269 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
270}
271
272
273/**
274 * List all available cloud images for the specified cloud provider.
275 *
276 * @returns RTEXITCODE
277 * @param a is the list of passed arguments
278 * @param iFirst is the position of the first unparsed argument in the arguments list
279 * @param pCommonOpts is a pointer to the structure CLOUDCOMMONOPT with some common
280 * arguments which have been already parsed before
281 */
282static RTEXITCODE listCloudImages(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts)
283{
284 static const RTGETOPTDEF s_aOptions[] =
285 {
286 { "--compartment-id", 'c', RTGETOPT_REQ_STRING },
287 { "--state", 's', RTGETOPT_REQ_STRING },
288 { "help", 1001, RTGETOPT_REQ_NOTHING },
289 { "--help", 1002, RTGETOPT_REQ_NOTHING }
290 };
291 RTGETOPTSTATE GetState;
292 RTGETOPTUNION ValueUnion;
293 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
294 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
295
296 Utf8Str strCompartmentId;
297 com::SafeArray<CloudImageState_T> imageStates;
298
299 int c;
300 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
301 {
302 switch (c)
303 {
304 case 'c':
305 strCompartmentId = ValueUnion.psz;
306 break;
307
308 case 's':
309 {
310 const char * const pszState = ValueUnion.psz;
311
312 if (RTStrICmp(pszState, "available") == 0)
313 imageStates.push_back(CloudImageState_Available);
314 else if (RTStrICmp(pszState, "deleted") == 0)
315 imageStates.push_back(CloudImageState_Deleted);
316 else if (RTStrICmp(pszState, "disabled") == 0)
317 imageStates.push_back(CloudImageState_Disabled);
318 else if (RTStrICmp(pszState, "exporting") == 0)
319 imageStates.push_back(CloudImageState_Exporting);
320 else if (RTStrICmp(pszState, "importing") == 0)
321 imageStates.push_back(CloudImageState_Importing);
322 else if (RTStrICmp(pszState, "provisioning") == 0)
323 imageStates.push_back(CloudImageState_Provisioning);
324 else
325 return errorArgument(Cloud::tr("Unknown cloud image state \"%s\""), pszState);
326 break;
327 }
328 case 1001:
329 case 1002:
330 printHelp(g_pStdOut);
331 return RTEXITCODE_SUCCESS;
332 case VINF_GETOPT_NOT_OPTION:
333 return errorUnknownSubcommand(ValueUnion.psz);
334
335 default:
336 return errorGetOpt(c, &ValueUnion);
337 }
338 }
339
340
341 HRESULT hrc = S_OK;
342
343 /* Delayed check. It allows us to print help information.*/
344 hrc = checkAndSetCommonOptions(a, pCommonOpts);
345 if (FAILED(hrc))
346 return RTEXITCODE_FAILURE;
347
348 ComPtr<IVirtualBox> pVirtualBox = a->virtualBox;
349
350 ComPtr<ICloudProviderManager> pCloudProviderManager;
351 CHECK_ERROR2_RET(hrc, pVirtualBox,
352 COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam()),
353 RTEXITCODE_FAILURE);
354
355 ComPtr<ICloudProvider> pCloudProvider;
356 CHECK_ERROR2_RET(hrc, pCloudProviderManager,
357 GetProviderByShortName(Bstr(pCommonOpts->provider.pszProviderName).raw(), pCloudProvider.asOutParam()),
358 RTEXITCODE_FAILURE);
359
360 ComPtr<ICloudProfile> pCloudProfile;
361 CHECK_ERROR2_RET(hrc, pCloudProvider,
362 GetProfileByName(Bstr(pCommonOpts->profile.pszProfileName).raw(), pCloudProfile.asOutParam()),
363 RTEXITCODE_FAILURE);
364
365 if (strCompartmentId.isNotEmpty())
366 {
367 CHECK_ERROR2_RET(hrc, pCloudProfile,
368 SetProperty(Bstr("compartment").raw(), Bstr(strCompartmentId).raw()),\
369 RTEXITCODE_FAILURE);
370 }
371 else
372 {
373 RTPrintf(Cloud::tr("Parameter \'compartment\' is empty or absent.\n"
374 "Trying to get the compartment from the passed cloud profile \'%s\'\n"),
375 pCommonOpts->profile.pszProfileName);
376 Bstr bStrCompartmentId;
377 CHECK_ERROR2_RET(hrc, pCloudProfile,
378 GetProperty(Bstr("compartment").raw(), bStrCompartmentId.asOutParam()),
379 RTEXITCODE_FAILURE);
380 strCompartmentId = bStrCompartmentId;
381 if (strCompartmentId.isNotEmpty())
382 RTPrintf(Cloud::tr("Found the compartment \'%s\':\n"), strCompartmentId.c_str());
383 else
384 return errorSyntax(USAGE_S_NEWCMD, Cloud::tr("Parameter --compartment-id is required"));
385 }
386
387 Bstr bstrProfileName;
388 pCloudProfile->COMGETTER(Name)(bstrProfileName.asOutParam());
389
390 ComObjPtr<ICloudClient> oCloudClient;
391 CHECK_ERROR2_RET(hrc, pCloudProfile,
392 CreateCloudClient(oCloudClient.asOutParam()),
393 RTEXITCODE_FAILURE);
394
395 ComPtr<IStringArray> pVMNamesHolder;
396 ComPtr<IStringArray> pVMIdsHolder;
397 com::SafeArray<BSTR> arrayVMNames;
398 com::SafeArray<BSTR> arrayVMIds;
399 ComPtr<IProgress> pProgress;
400
401 RTPrintf(Cloud::tr("Reply is in the form \'image name\' = \'image id\'\n"));
402 CHECK_ERROR2_RET(hrc, oCloudClient,
403 ListImages(ComSafeArrayAsInParam(imageStates),
404 pVMNamesHolder.asOutParam(),
405 pVMIdsHolder.asOutParam(),
406 pProgress.asOutParam()),
407 RTEXITCODE_FAILURE);
408 showProgress(pProgress);
409 CHECK_PROGRESS_ERROR_RET(pProgress, (Cloud::tr("Failed to list images")), RTEXITCODE_FAILURE);
410
411 CHECK_ERROR2_RET(hrc,
412 pVMNamesHolder, COMGETTER(Values)(ComSafeArrayAsOutParam(arrayVMNames)),
413 RTEXITCODE_FAILURE);
414 CHECK_ERROR2_RET(hrc,
415 pVMIdsHolder, COMGETTER(Values)(ComSafeArrayAsOutParam(arrayVMIds)),
416 RTEXITCODE_FAILURE);
417
418 RTPrintf(Cloud::tr("The list of the images for the cloud profile \'%ls\'\nand compartment \'%s\':\n"),
419 bstrProfileName.raw(), strCompartmentId.c_str());
420 size_t cNames = arrayVMNames.size();
421 size_t cIds = arrayVMIds.size();
422 for (size_t k = 0; k < cNames; k++)
423 {
424 Bstr value;
425 if (k < cIds)
426 value = arrayVMIds[k];
427 RTPrintf("\t%ls = %ls\n", arrayVMNames[k], value.raw());
428 }
429
430 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
431}
432
433/**
434 * General function which handles the "list" commands
435 *
436 * @returns RTEXITCODE
437 * @param a is the list of passed arguments
438 * @param iFirst is the position of the first unparsed argument in the arguments list
439 * @param pCommonOpts is a pointer to the structure CLOUDCOMMONOPT with some common
440 * arguments which have been already parsed before
441 */
442static RTEXITCODE handleCloudLists(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts)
443{
444 enum
445 {
446 kCloudListIota = 1000,
447 kCloudList_Images,
448 kCloudList_Instances,
449 kCloudList_Machines,
450 kCloudList_Networks,
451 kCloudList_Objects,
452 kCloudList_Subnets,
453 kCloudList_Vcns,
454 };
455
456 static const RTGETOPTDEF s_aOptions[] =
457 {
458 { "images", kCloudList_Images, RTGETOPT_REQ_NOTHING },
459 { "instances", kCloudList_Instances, RTGETOPT_REQ_NOTHING },
460 { "machines", kCloudList_Machines, RTGETOPT_REQ_NOTHING },
461 { "networks", kCloudList_Networks, RTGETOPT_REQ_NOTHING },
462 { "objects", kCloudList_Objects, RTGETOPT_REQ_NOTHING },
463 { "subnets", kCloudList_Subnets, RTGETOPT_REQ_NOTHING },
464 { "vcns", kCloudList_Vcns, RTGETOPT_REQ_NOTHING },
465 { "vms", kCloudList_Machines, RTGETOPT_REQ_NOTHING },
466
467 { "help", 'h', RTGETOPT_REQ_NOTHING },
468 { "-?", 'h', RTGETOPT_REQ_NOTHING },
469 { "-help", 'h', RTGETOPT_REQ_NOTHING },
470 { "--help", 'h', RTGETOPT_REQ_NOTHING },
471 };
472
473 if (a->argc == iFirst)
474 {
475 RTPrintf(Cloud::tr("Empty command parameter list, show help.\n"));
476 printHelp(g_pStdOut);
477 return RTEXITCODE_SUCCESS;
478 }
479
480 RTGETOPTSTATE GetState;
481 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
482 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
483
484 int c;
485 RTGETOPTUNION ValueUnion;
486 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
487 {
488 switch (c)
489 {
490 case kCloudList_Images:
491 setCurrentSubcommand(HELP_SCOPE_CLOUDLIST_IMAGES);
492 return listCloudImages(a, GetState.iNext, pCommonOpts);
493
494 case kCloudList_Instances:
495 setCurrentSubcommand(HELP_SCOPE_CLOUDLIST_INSTANCES);
496 return listCloudInstances(a, GetState.iNext, pCommonOpts);
497
498 case kCloudList_Machines:
499 return listCloudMachines(a, GetState.iNext,
500 pCommonOpts->provider.pszProviderName,
501 pCommonOpts->profile.pszProfileName);
502
503 case 'h':
504 printHelp(g_pStdOut);
505 return RTEXITCODE_SUCCESS;
506
507 case VINF_GETOPT_NOT_OPTION:
508 return errorUnknownSubcommand(ValueUnion.psz);
509
510 default:
511 return errorGetOpt(c, &ValueUnion);
512 }
513 }
514
515 return errorNoSubcommand();
516}
517
518static RTEXITCODE createCloudInstance(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts)
519{
520 HRESULT hrc = S_OK;
521
522 static const RTGETOPTDEF s_aOptions[] =
523 {
524 { "--image-id", 'i', RTGETOPT_REQ_STRING },
525 { "--boot-volume-id", 'v', RTGETOPT_REQ_STRING },
526 { "--display-name", 'n', RTGETOPT_REQ_STRING },
527 { "--launch-mode", 'm', RTGETOPT_REQ_STRING },
528 { "--shape", 's', RTGETOPT_REQ_STRING },
529 { "--domain-name", 'd', RTGETOPT_REQ_STRING },
530 { "--boot-disk-size", 'b', RTGETOPT_REQ_STRING },
531 { "--publicip", 'p', RTGETOPT_REQ_STRING },
532 { "--subnet", 't', RTGETOPT_REQ_STRING },
533 { "--privateip", 'P', RTGETOPT_REQ_STRING },
534 { "--launch", 'l', RTGETOPT_REQ_STRING },
535 { "--public-ssh-key", 'k', RTGETOPT_REQ_STRING },
536 { "--cloud-init-script-path", 'c', RTGETOPT_REQ_STRING },
537 { "help", 1001, RTGETOPT_REQ_NOTHING },
538 { "--help", 1002, RTGETOPT_REQ_NOTHING }
539 };
540 RTGETOPTSTATE GetState;
541 RTGETOPTUNION ValueUnion;
542 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
543 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
544 if (a->argc == iFirst)
545 {
546 RTPrintf(Cloud::tr("Empty command parameter list, show help.\n"));
547 printHelp(g_pStdOut);
548 return RTEXITCODE_SUCCESS;
549 }
550
551 ComPtr<IAppliance> pAppliance;
552 CHECK_ERROR2_RET(hrc, a->virtualBox, CreateAppliance(pAppliance.asOutParam()), RTEXITCODE_FAILURE);
553 ULONG vsdNum = 1;
554 CHECK_ERROR2_RET(hrc, pAppliance, CreateVirtualSystemDescriptions(1, &vsdNum), RTEXITCODE_FAILURE);
555 com::SafeIfaceArray<IVirtualSystemDescription> virtualSystemDescriptions;
556 CHECK_ERROR2_RET(hrc, pAppliance,
557 COMGETTER(VirtualSystemDescriptions)(ComSafeArrayAsOutParam(virtualSystemDescriptions)),
558 RTEXITCODE_FAILURE);
559 ComPtr<IVirtualSystemDescription> pVSD = virtualSystemDescriptions[0];
560
561 Utf8Str strDisplayName, strImageId, strBootVolumeId, strPublicSSHKey;
562 int c;
563 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
564 {
565 switch (c)
566 {
567 case 'i':
568 strImageId = ValueUnion.psz;
569 pVSD->AddDescription(VirtualSystemDescriptionType_CloudImageId,
570 Bstr(ValueUnion.psz).raw(), NULL);
571 break;
572
573 case 'v':
574 strBootVolumeId = ValueUnion.psz;
575 pVSD->AddDescription(VirtualSystemDescriptionType_CloudBootVolumeId,
576 Bstr(ValueUnion.psz).raw(), NULL);
577 break;
578 case 'n':
579 strDisplayName = ValueUnion.psz;
580 pVSD->AddDescription(VirtualSystemDescriptionType_Name,
581 Bstr(ValueUnion.psz).raw(), NULL);
582 break;
583 case 'm':
584 pVSD->AddDescription(VirtualSystemDescriptionType_CloudOCILaunchMode,
585 Bstr(ValueUnion.psz).raw(), NULL);
586 break;
587 case 's':
588 pVSD->AddDescription(VirtualSystemDescriptionType_CloudInstanceShape,
589 Bstr(ValueUnion.psz).raw(), NULL);
590 break;
591 case 'd':
592 pVSD->AddDescription(VirtualSystemDescriptionType_CloudDomain,
593 Bstr(ValueUnion.psz).raw(), NULL);
594 break;
595 case 'b':
596 pVSD->AddDescription(VirtualSystemDescriptionType_CloudBootDiskSize,
597 Bstr(ValueUnion.psz).raw(), NULL);
598 break;
599 case 'p':
600 pVSD->AddDescription(VirtualSystemDescriptionType_CloudPublicIP,
601 Bstr(ValueUnion.psz).raw(), NULL);
602 break;
603 case 'P':
604 pVSD->AddDescription(VirtualSystemDescriptionType_CloudPrivateIP,
605 Bstr(ValueUnion.psz).raw(), NULL);
606 break;
607 case 't':
608 pVSD->AddDescription(VirtualSystemDescriptionType_CloudOCISubnet,
609 Bstr(ValueUnion.psz).raw(), NULL);
610 break;
611 case 'l':
612 {
613 Utf8Str strLaunch(ValueUnion.psz);
614 if (strLaunch.isNotEmpty() && (strLaunch.equalsIgnoreCase("true") || strLaunch.equalsIgnoreCase("false")))
615 pVSD->AddDescription(VirtualSystemDescriptionType_CloudLaunchInstance,
616 Bstr(ValueUnion.psz).raw(), NULL);
617 break;
618 }
619 case 'k':
620 strPublicSSHKey = ValueUnion.psz;
621 pVSD->AddDescription(VirtualSystemDescriptionType_CloudPublicSSHKey,
622 Bstr(ValueUnion.psz).raw(), NULL);
623 break;
624 case 'c':
625 pVSD->AddDescription(VirtualSystemDescriptionType_CloudInitScriptPath,
626 Bstr(ValueUnion.psz).raw(), NULL);
627 break;
628 case 1001:
629 case 1002:
630 printHelp(g_pStdOut);
631 return RTEXITCODE_SUCCESS;
632 case VINF_GETOPT_NOT_OPTION:
633 return errorUnknownSubcommand(ValueUnion.psz);
634 default:
635 return errorGetOpt(c, &ValueUnion);
636 }
637 }
638
639 /* Delayed check. It allows us to print help information.*/
640 hrc = checkAndSetCommonOptions(a, pCommonOpts);
641 if (FAILED(hrc))
642 return RTEXITCODE_FAILURE;
643
644 if (strPublicSSHKey.isEmpty())
645 RTPrintf(Cloud::tr("Warning!!! Public SSH key doesn't present in the passed arguments...\n"));
646
647 if (strImageId.isNotEmpty() && strBootVolumeId.isNotEmpty())
648 return errorArgument(Cloud::tr("Parameters --image-id and --boot-volume-id are mutually exclusive. "
649 "Only one of them must be presented."));
650
651 if (strImageId.isEmpty() && strBootVolumeId.isEmpty())
652 return errorArgument(Cloud::tr("Missing parameter --image-id or --boot-volume-id. One of them must be presented."));
653
654 ComPtr<ICloudProfile> pCloudProfile = pCommonOpts->profile.pCloudProfile;
655
656 pVSD->AddDescription(VirtualSystemDescriptionType_CloudProfileName,
657 Bstr(pCommonOpts->profile.pszProfileName).raw(),
658 NULL);
659
660 ComObjPtr<ICloudClient> oCloudClient;
661 CHECK_ERROR2_RET(hrc, pCloudProfile,
662 CreateCloudClient(oCloudClient.asOutParam()),
663 RTEXITCODE_FAILURE);
664
665 ComPtr<IStringArray> infoArray;
666 com::SafeArray<BSTR> pStrInfoArray;
667 ComPtr<IProgress> pProgress;
668
669#if 0
670 /*
671 * OCI API returns an error during an instance creation if the image isn't available
672 * or in the inappropriate state. So the check can be omitted.
673 */
674 RTPrintf(Cloud::tr("Checking the cloud image with id \'%s\'...\n"), strImageId.c_str());
675 CHECK_ERROR2_RET(hrc, oCloudClient,
676 GetImageInfo(Bstr(strImageId).raw(),
677 infoArray.asOutParam(),
678 pProgress.asOutParam()),
679 RTEXITCODE_FAILURE);
680
681 hrc = showProgress(pProgress);
682 CHECK_PROGRESS_ERROR_RET(pProgress, (Cloud::tr("Checking the cloud image failed")), RTEXITCODE_FAILURE);
683
684 pProgress.setNull();
685#endif
686
687 if (strImageId.isNotEmpty())
688 RTPrintf(Cloud::tr("Creating cloud instance with name \'%s\' from the image \'%s\'...\n"),
689 strDisplayName.c_str(), strImageId.c_str());
690 else
691 RTPrintf(Cloud::tr("Creating cloud instance with name \'%s\' from the boot volume \'%s\'...\n"),
692 strDisplayName.c_str(), strBootVolumeId.c_str());
693
694 CHECK_ERROR2_RET(hrc, oCloudClient, LaunchVM(pVSD, pProgress.asOutParam()), RTEXITCODE_FAILURE);
695
696 hrc = showProgress(pProgress);
697 CHECK_PROGRESS_ERROR_RET(pProgress, (Cloud::tr("Creating cloud instance failed")), RTEXITCODE_FAILURE);
698
699 if (SUCCEEDED(hrc))
700 RTPrintf(Cloud::tr("Cloud instance was created successfully\n"));
701
702 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
703}
704
705static RTEXITCODE updateCloudInstance(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts)
706{
707 RT_NOREF(a);
708 RT_NOREF(iFirst);
709 RT_NOREF(pCommonOpts);
710 return RTEXITCODE_SUCCESS;
711}
712
713static RTEXITCODE showCloudInstanceInfo(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts)
714{
715 HRESULT hrc = S_OK;
716
717 static const RTGETOPTDEF s_aOptions[] =
718 {
719 { "--id", 'i', RTGETOPT_REQ_STRING },
720 { "help", 1001, RTGETOPT_REQ_NOTHING },
721 { "--help", 1002, RTGETOPT_REQ_NOTHING }
722 };
723 RTGETOPTSTATE GetState;
724 RTGETOPTUNION ValueUnion;
725 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
726 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
727 if (a->argc == iFirst)
728 {
729 RTPrintf(Cloud::tr("Empty command parameter list, show help.\n"));
730 printHelp(g_pStdOut);
731 return RTEXITCODE_SUCCESS;
732 }
733
734 Utf8Str strInstanceId;
735
736 int c;
737 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
738 {
739 switch (c)
740 {
741 case 'i':
742 {
743 if (strInstanceId.isNotEmpty())
744 return errorArgument(Cloud::tr("Duplicate parameter: --id"));
745
746 strInstanceId = ValueUnion.psz;
747 if (strInstanceId.isEmpty())
748 return errorArgument(Cloud::tr("Empty parameter: --id"));
749
750 break;
751 }
752 case 1001:
753 case 1002:
754 printHelp(g_pStdOut);
755 return RTEXITCODE_SUCCESS;
756 case VINF_GETOPT_NOT_OPTION:
757 return errorUnknownSubcommand(ValueUnion.psz);
758
759 default:
760 return errorGetOpt(c, &ValueUnion);
761 }
762 }
763
764 /* Delayed check. It allows us to print help information.*/
765 hrc = checkAndSetCommonOptions(a, pCommonOpts);
766 if (FAILED(hrc))
767 return RTEXITCODE_FAILURE;
768
769 if (strInstanceId.isEmpty())
770 return errorArgument(Cloud::tr("Missing parameter: --id"));
771
772 ComPtr<ICloudProfile> pCloudProfile = pCommonOpts->profile.pCloudProfile;
773
774 ComObjPtr<ICloudClient> oCloudClient;
775 CHECK_ERROR2_RET(hrc, pCloudProfile,
776 CreateCloudClient(oCloudClient.asOutParam()),
777 RTEXITCODE_FAILURE);
778 RTPrintf(Cloud::tr("Getting information about cloud instance with id %s...\n"), strInstanceId.c_str());
779 RTPrintf(Cloud::tr("Reply is in the form \'setting name\' = \'value\'\n"));
780
781 ComPtr<IAppliance> pAppliance;
782 CHECK_ERROR2_RET(hrc, a->virtualBox, CreateAppliance(pAppliance.asOutParam()), RTEXITCODE_FAILURE);
783
784 com::SafeIfaceArray<IVirtualSystemDescription> vsdArray;
785 ULONG requestedVSDnums = 1;
786 ULONG newVSDnums = 0;
787 CHECK_ERROR2_RET(hrc, pAppliance, CreateVirtualSystemDescriptions(requestedVSDnums, &newVSDnums), RTEXITCODE_FAILURE);
788 if (requestedVSDnums != newVSDnums)
789 return RTEXITCODE_FAILURE;
790
791 CHECK_ERROR2_RET(hrc, pAppliance, COMGETTER(VirtualSystemDescriptions)(ComSafeArrayAsOutParam(vsdArray)), RTEXITCODE_FAILURE);
792 ComPtr<IVirtualSystemDescription> instanceDescription = vsdArray[0];
793
794 ComPtr<IProgress> progress;
795 CHECK_ERROR2_RET(hrc, oCloudClient,
796 GetInstanceInfo(Bstr(strInstanceId).raw(), instanceDescription, progress.asOutParam()),
797 RTEXITCODE_FAILURE);
798
799 hrc = showProgress(progress);
800 CHECK_PROGRESS_ERROR_RET(progress, (Cloud::tr("Getting information about cloud instance failed")), RTEXITCODE_FAILURE);
801
802 RTPrintf(Cloud::tr("Cloud instance info (provider '%s'):\n"),
803 pCommonOpts->provider.pszProviderName);
804
805 struct vsdHReadable {
806 VirtualSystemDescriptionType_T vsdType;
807 Utf8Str strFound;
808 Utf8Str strNotFound;
809 };
810
811 const size_t vsdHReadableArraySize = 13;//the number of items in the vsdHReadableArray
812 vsdHReadable vsdHReadableArray[vsdHReadableArraySize] = {
813 {VirtualSystemDescriptionType_CloudDomain, Cloud::tr("Availability domain = %ls\n"), Cloud::tr("Availability domain wasn't found\n")},
814 {VirtualSystemDescriptionType_Name, Cloud::tr("Instance displayed name = %ls\n"), Cloud::tr("Instance displayed name wasn't found\n")},
815 {VirtualSystemDescriptionType_CloudInstanceState, Cloud::tr("Instance state = %ls\n"), Cloud::tr("Instance state wasn't found\n")},
816 {VirtualSystemDescriptionType_CloudInstanceId, Cloud::tr("Instance Id = %ls\n"), Cloud::tr("Instance Id wasn't found\n")},
817 {VirtualSystemDescriptionType_CloudInstanceDisplayName, Cloud::tr("Instance name = %ls\n"), Cloud::tr("Instance name wasn't found\n")},
818 {VirtualSystemDescriptionType_CloudImageId, Cloud::tr("Bootable image Id = %ls\n"),
819 Cloud::tr("Image Id whom the instance is booted up wasn't found\n")},
820 {VirtualSystemDescriptionType_CloudInstanceShape, Cloud::tr("Shape of the instance = %ls\n"),
821 Cloud::tr("The shape of the instance wasn't found\n")},
822 {VirtualSystemDescriptionType_OS, Cloud::tr("Type of guest OS = %ls\n"), Cloud::tr("Type of guest OS wasn't found\n")},
823 {VirtualSystemDescriptionType_Memory, Cloud::tr("RAM = %ls MB\n"), Cloud::tr("Value for RAM wasn't found\n")},
824 {VirtualSystemDescriptionType_CPU, Cloud::tr("CPUs = %ls\n"), Cloud::tr("Numbers of CPUs weren't found\n")},
825 {VirtualSystemDescriptionType_CloudPublicIP, Cloud::tr("Instance public IP = %ls\n"), Cloud::tr("Public IP wasn't found\n")},
826 {VirtualSystemDescriptionType_Miscellaneous, "%ls\n", Cloud::tr("Free-form tags or metadata weren't found\n")},
827 {VirtualSystemDescriptionType_CloudInitScriptPath, "%ls\n", Cloud::tr("Cloud-init script wasn't found\n")}
828 };
829
830 com::SafeArray<VirtualSystemDescriptionType_T> retTypes;
831 com::SafeArray<BSTR> aRefs;
832 com::SafeArray<BSTR> aOvfValues;
833 com::SafeArray<BSTR> aVBoxValues;
834 com::SafeArray<BSTR> aExtraConfigValues;
835
836 for (size_t i=0; i<vsdHReadableArraySize ; ++i)
837 {
838 hrc = instanceDescription->GetDescriptionByType(vsdHReadableArray[i].vsdType,
839 ComSafeArrayAsOutParam(retTypes),
840 ComSafeArrayAsOutParam(aRefs),
841 ComSafeArrayAsOutParam(aOvfValues),
842 ComSafeArrayAsOutParam(aVBoxValues),
843 ComSafeArrayAsOutParam(aExtraConfigValues));
844 if (FAILED(hrc) || aVBoxValues.size() == 0)
845 LogRel((vsdHReadableArray[i].strNotFound.c_str()));
846 else
847 {
848 LogRel(("Size is %d", aVBoxValues.size()));
849 for (size_t j = 0; j<aVBoxValues.size(); ++j)
850 {
851 RTPrintf(vsdHReadableArray[i].strFound.c_str(), aVBoxValues[j]);
852 }
853 }
854
855 retTypes.setNull();
856 aRefs.setNull();
857 aOvfValues.setNull();
858 aVBoxValues.setNull();
859 aExtraConfigValues.setNull();
860 }
861
862 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
863}
864
865static RTEXITCODE startCloudInstance(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts)
866{
867 HRESULT hrc = S_OK;
868
869 static const RTGETOPTDEF s_aOptions[] =
870 {
871 { "--id", 'i', RTGETOPT_REQ_STRING },
872 { "help", 1001, RTGETOPT_REQ_NOTHING },
873 { "--help", 1002, RTGETOPT_REQ_NOTHING }
874 };
875 RTGETOPTSTATE GetState;
876 RTGETOPTUNION ValueUnion;
877 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
878 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
879 if (a->argc == iFirst)
880 {
881 RTPrintf(Cloud::tr("Empty command parameter list, show help.\n"));
882 printHelp(g_pStdOut);
883 return RTEXITCODE_SUCCESS;
884 }
885
886 Utf8Str strInstanceId;
887
888 int c;
889 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
890 {
891 switch (c)
892 {
893 case 'i':
894 {
895 if (strInstanceId.isNotEmpty())
896 return errorArgument(Cloud::tr("Duplicate parameter: --id"));
897
898 strInstanceId = ValueUnion.psz;
899 if (strInstanceId.isEmpty())
900 return errorArgument(Cloud::tr("Empty parameter: --id"));
901
902 break;
903 }
904 case 1001:
905 case 1002:
906 printHelp(g_pStdOut);
907 return RTEXITCODE_SUCCESS;
908 case VINF_GETOPT_NOT_OPTION:
909 return errorUnknownSubcommand(ValueUnion.psz);
910
911 default:
912 return errorGetOpt(c, &ValueUnion);
913 }
914 }
915
916 /* Delayed check. It allows us to print help information.*/
917 hrc = checkAndSetCommonOptions(a, pCommonOpts);
918 if (FAILED(hrc))
919 return RTEXITCODE_FAILURE;
920
921 if (strInstanceId.isEmpty())
922 return errorArgument(Cloud::tr("Missing parameter: --id"));
923
924 ComPtr<ICloudProfile> pCloudProfile = pCommonOpts->profile.pCloudProfile;
925
926 ComObjPtr<ICloudClient> oCloudClient;
927 CHECK_ERROR2_RET(hrc, pCloudProfile,
928 CreateCloudClient(oCloudClient.asOutParam()),
929 RTEXITCODE_FAILURE);
930 RTPrintf(Cloud::tr("Starting cloud instance with id %s...\n"), strInstanceId.c_str());
931
932 ComPtr<IProgress> progress;
933 CHECK_ERROR2_RET(hrc, oCloudClient,
934 StartInstance(Bstr(strInstanceId).raw(), progress.asOutParam()),
935 RTEXITCODE_FAILURE);
936 hrc = showProgress(progress);
937 CHECK_PROGRESS_ERROR_RET(progress, (Cloud::tr("Starting the cloud instance failed")), RTEXITCODE_FAILURE);
938
939 if (SUCCEEDED(hrc))
940 RTPrintf(Cloud::tr("Cloud instance with id %s (provider = '%s', profile = '%s') was started\n"),
941 strInstanceId.c_str(),
942 pCommonOpts->provider.pszProviderName,
943 pCommonOpts->profile.pszProfileName);
944
945 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
946}
947
948static RTEXITCODE pauseCloudInstance(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts)
949{
950 HRESULT hrc = S_OK;
951
952 static const RTGETOPTDEF s_aOptions[] =
953 {
954 { "--id", 'i', RTGETOPT_REQ_STRING },
955 { "help", 1001, RTGETOPT_REQ_NOTHING },
956 { "--help", 1002, RTGETOPT_REQ_NOTHING }
957 };
958 RTGETOPTSTATE GetState;
959 RTGETOPTUNION ValueUnion;
960 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
961 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
962 if (a->argc == iFirst)
963 {
964 RTPrintf(Cloud::tr("Empty command parameter list, show help.\n"));
965 printHelp(g_pStdOut);
966 return RTEXITCODE_SUCCESS;
967 }
968
969 Utf8Str strInstanceId;
970
971 int c;
972 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
973 {
974 switch (c)
975 {
976 case 'i':
977 {
978 if (strInstanceId.isNotEmpty())
979 return errorArgument(Cloud::tr("Duplicate parameter: --id"));
980
981 strInstanceId = ValueUnion.psz;
982 if (strInstanceId.isEmpty())
983 return errorArgument(Cloud::tr("Empty parameter: --id"));
984
985 break;
986 }
987 case 1001:
988 case 1002:
989 printHelp(g_pStdOut);
990 return RTEXITCODE_SUCCESS;
991 case VINF_GETOPT_NOT_OPTION:
992 return errorUnknownSubcommand(ValueUnion.psz);
993
994 default:
995 return errorGetOpt(c, &ValueUnion);
996 }
997 }
998
999 /* Delayed check. It allows us to print help information.*/
1000 hrc = checkAndSetCommonOptions(a, pCommonOpts);
1001 if (FAILED(hrc))
1002 return RTEXITCODE_FAILURE;
1003
1004 if (strInstanceId.isEmpty())
1005 return errorArgument(Cloud::tr("Missing parameter: --id"));
1006
1007 ComPtr<ICloudProfile> pCloudProfile = pCommonOpts->profile.pCloudProfile;
1008
1009 ComObjPtr<ICloudClient> oCloudClient;
1010 CHECK_ERROR2_RET(hrc, pCloudProfile,
1011 CreateCloudClient(oCloudClient.asOutParam()),
1012 RTEXITCODE_FAILURE);
1013 RTPrintf(Cloud::tr("Pausing cloud instance with id %s...\n"), strInstanceId.c_str());
1014
1015 ComPtr<IProgress> progress;
1016 CHECK_ERROR2_RET(hrc, oCloudClient,
1017 PauseInstance(Bstr(strInstanceId).raw(), progress.asOutParam()),
1018 RTEXITCODE_FAILURE);
1019 hrc = showProgress(progress);
1020 CHECK_PROGRESS_ERROR_RET(progress, (Cloud::tr("Pause the cloud instance failed")), RTEXITCODE_FAILURE);
1021
1022 if (SUCCEEDED(hrc))
1023 RTPrintf(Cloud::tr("Cloud instance with id %s (provider = '%s', profile = '%s') was paused\n"),
1024 strInstanceId.c_str(),
1025 pCommonOpts->provider.pszProviderName,
1026 pCommonOpts->profile.pszProfileName);
1027
1028 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1029}
1030
1031static RTEXITCODE terminateCloudInstance(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts)
1032{
1033 HRESULT hrc = S_OK;
1034
1035 static const RTGETOPTDEF s_aOptions[] =
1036 {
1037 { "--id", 'i', RTGETOPT_REQ_STRING },
1038 { "help", 1001, RTGETOPT_REQ_NOTHING },
1039 { "--help", 1002, RTGETOPT_REQ_NOTHING }
1040 };
1041 RTGETOPTSTATE GetState;
1042 RTGETOPTUNION ValueUnion;
1043 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
1044 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
1045 if (a->argc == iFirst)
1046 {
1047 RTPrintf(Cloud::tr("Empty command parameter list, show help.\n"));
1048 printHelp(g_pStdOut);
1049 return RTEXITCODE_SUCCESS;
1050 }
1051
1052 Utf8Str strInstanceId;
1053
1054 int c;
1055 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
1056 {
1057 switch (c)
1058 {
1059 case 'i':
1060 {
1061 if (strInstanceId.isNotEmpty())
1062 return errorArgument(Cloud::tr("Duplicate parameter: --id"));
1063
1064 strInstanceId = ValueUnion.psz;
1065 if (strInstanceId.isEmpty())
1066 return errorArgument(Cloud::tr("Empty parameter: --id"));
1067
1068 break;
1069 }
1070 case 1001:
1071 case 1002:
1072 printHelp(g_pStdOut);
1073 return RTEXITCODE_SUCCESS;
1074 case VINF_GETOPT_NOT_OPTION:
1075 return errorUnknownSubcommand(ValueUnion.psz);
1076
1077 default:
1078 return errorGetOpt(c, &ValueUnion);
1079 }
1080 }
1081
1082 /* Delayed check. It allows us to print help information.*/
1083 hrc = checkAndSetCommonOptions(a, pCommonOpts);
1084 if (FAILED(hrc))
1085 return RTEXITCODE_FAILURE;
1086
1087 if (strInstanceId.isEmpty())
1088 return errorArgument(Cloud::tr("Missing parameter: --id"));
1089
1090
1091 ComPtr<ICloudProfile> pCloudProfile = pCommonOpts->profile.pCloudProfile;
1092
1093 ComObjPtr<ICloudClient> oCloudClient;
1094 CHECK_ERROR2_RET(hrc, pCloudProfile,
1095 CreateCloudClient(oCloudClient.asOutParam()),
1096 RTEXITCODE_FAILURE);
1097 RTPrintf(Cloud::tr("Terminating cloud instance with id %s...\n"), strInstanceId.c_str());
1098
1099 ComPtr<IProgress> progress;
1100 CHECK_ERROR2_RET(hrc, oCloudClient,
1101 TerminateInstance(Bstr(strInstanceId).raw(), progress.asOutParam()),
1102 RTEXITCODE_FAILURE);
1103 hrc = showProgress(progress);
1104 CHECK_PROGRESS_ERROR_RET(progress, (Cloud::tr("Termination the cloud instance failed")), RTEXITCODE_FAILURE);
1105
1106 if (SUCCEEDED(hrc))
1107 RTPrintf(Cloud::tr("Cloud instance with id %s (provider = '%s', profile = '%s') was terminated\n"),
1108 strInstanceId.c_str(),
1109 pCommonOpts->provider.pszProviderName,
1110 pCommonOpts->profile.pszProfileName);
1111
1112 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1113}
1114
1115static RTEXITCODE handleCloudInstance(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts)
1116{
1117 enum
1118 {
1119 kCloudInstanceIota = 1000,
1120 kCloudInstance_Create,
1121 kCloudInstance_Info,
1122 kCloudInstance_Pause,
1123 kCloudInstance_Start,
1124 kCloudInstance_Terminate,
1125 kCloudInstance_Update,
1126 };
1127
1128 static const RTGETOPTDEF s_aOptions[] =
1129 {
1130 { "create", kCloudInstance_Create, RTGETOPT_REQ_NOTHING },
1131 { "info", kCloudInstance_Info, RTGETOPT_REQ_NOTHING },
1132 { "pause", kCloudInstance_Pause, RTGETOPT_REQ_NOTHING },
1133 { "start", kCloudInstance_Start, RTGETOPT_REQ_NOTHING },
1134 { "terminate", kCloudInstance_Terminate, RTGETOPT_REQ_NOTHING },
1135 { "update", kCloudInstance_Update, RTGETOPT_REQ_NOTHING },
1136
1137 { "help", 'h', RTGETOPT_REQ_NOTHING },
1138 { "-?", 'h', RTGETOPT_REQ_NOTHING },
1139 { "-help", 'h', RTGETOPT_REQ_NOTHING },
1140 { "--help", 'h', RTGETOPT_REQ_NOTHING },
1141 };
1142
1143 if (a->argc == iFirst)
1144 {
1145 RTPrintf(Cloud::tr("Empty command parameter list, show help.\n"));
1146 printHelp(g_pStdOut);
1147 return RTEXITCODE_SUCCESS;
1148 }
1149
1150 RTGETOPTSTATE GetState;
1151 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
1152 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
1153
1154 int c;
1155 RTGETOPTUNION ValueUnion;
1156 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
1157 {
1158 switch (c)
1159 {
1160 /* Sub-commands: */
1161 case kCloudInstance_Create:
1162 setCurrentSubcommand(HELP_SCOPE_CLOUDINSTANCE_CREATE);
1163 return createCloudInstance(a, GetState.iNext, pCommonOpts);
1164
1165 case kCloudInstance_Start:
1166 setCurrentSubcommand(HELP_SCOPE_CLOUDINSTANCE_START);
1167 return startCloudInstance(a, GetState.iNext, pCommonOpts);
1168
1169 case kCloudInstance_Pause:
1170 setCurrentSubcommand(HELP_SCOPE_CLOUDINSTANCE_PAUSE);
1171 return pauseCloudInstance(a, GetState.iNext, pCommonOpts);
1172
1173 case kCloudInstance_Info:
1174 setCurrentSubcommand(HELP_SCOPE_CLOUDINSTANCE_INFO);
1175 return showCloudInstanceInfo(a, GetState.iNext, pCommonOpts);
1176
1177 case kCloudInstance_Update:
1178// setCurrentSubcommand(HELP_SCOPE_CLOUDINSTANCE_UPDATE);
1179 return updateCloudInstance(a, GetState.iNext, pCommonOpts);
1180
1181 case kCloudInstance_Terminate:
1182 setCurrentSubcommand(HELP_SCOPE_CLOUDINSTANCE_TERMINATE);
1183 return terminateCloudInstance(a, GetState.iNext, pCommonOpts);
1184
1185 case 'h':
1186 printHelp(g_pStdOut);
1187 return RTEXITCODE_SUCCESS;
1188
1189 case VINF_GETOPT_NOT_OPTION:
1190 return errorUnknownSubcommand(ValueUnion.psz);
1191
1192 default:
1193 return errorGetOpt(c, &ValueUnion);
1194 }
1195 }
1196
1197 return errorNoSubcommand();
1198}
1199
1200
1201static RTEXITCODE createCloudImage(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts)
1202{
1203 HRESULT hrc = S_OK;
1204
1205 static const RTGETOPTDEF s_aOptions[] =
1206 {
1207 { "--object-name", 'o', RTGETOPT_REQ_STRING },
1208 { "--bucket-name", 'b', RTGETOPT_REQ_STRING },
1209 { "--compartment-id", 'c', RTGETOPT_REQ_STRING },
1210 { "--instance-id", 'i', RTGETOPT_REQ_STRING },
1211 { "--display-name", 'd', RTGETOPT_REQ_STRING },
1212 { "--launch-mode", 'm', RTGETOPT_REQ_STRING },
1213 { "help", 1001, RTGETOPT_REQ_NOTHING },
1214 { "--help", 1002, RTGETOPT_REQ_NOTHING }
1215 };
1216 RTGETOPTSTATE GetState;
1217 RTGETOPTUNION ValueUnion;
1218 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
1219 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
1220 if (a->argc == iFirst)
1221 {
1222 RTPrintf(Cloud::tr("Empty command parameter list, show help.\n"));
1223 printHelp(g_pStdOut);
1224 return RTEXITCODE_SUCCESS;
1225 }
1226
1227 Utf8Str strCompartmentId;
1228 Utf8Str strInstanceId;
1229 Utf8Str strDisplayName;
1230 Utf8Str strBucketName;
1231 Utf8Str strObjectName;
1232 com::SafeArray<BSTR> parameters;
1233
1234 int c;
1235 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
1236 {
1237 switch (c)
1238 {
1239 case 'c':
1240 strCompartmentId=ValueUnion.psz;
1241 Bstr(Utf8Str("compartment-id=").append(ValueUnion.psz)).detachTo(parameters.appendedRaw());
1242 break;
1243 case 'i':
1244 strInstanceId=ValueUnion.psz;
1245 Bstr(Utf8Str("instance-id=").append(ValueUnion.psz)).detachTo(parameters.appendedRaw());
1246 break;
1247 case 'd':
1248 strDisplayName=ValueUnion.psz;
1249 Bstr(Utf8Str("display-name=").append(ValueUnion.psz)).detachTo(parameters.appendedRaw());
1250 break;
1251 case 'o':
1252 strObjectName=ValueUnion.psz;
1253 Bstr(Utf8Str("object-name=").append(ValueUnion.psz)).detachTo(parameters.appendedRaw());
1254 break;
1255 case 'b':
1256 strBucketName=ValueUnion.psz;
1257 Bstr(Utf8Str("bucket-name=").append(ValueUnion.psz)).detachTo(parameters.appendedRaw());
1258 break;
1259 case 'm':
1260 strBucketName=ValueUnion.psz;
1261 Bstr(Utf8Str("launch-mode=").append(ValueUnion.psz)).detachTo(parameters.appendedRaw());
1262 break;
1263 case 1001:
1264 case 1002:
1265 printHelp(g_pStdOut);
1266 return RTEXITCODE_SUCCESS;
1267 case VINF_GETOPT_NOT_OPTION:
1268 return errorUnknownSubcommand(ValueUnion.psz);
1269 default:
1270 return errorGetOpt(c, &ValueUnion);
1271 }
1272 }
1273
1274 /* Delayed check. It allows us to print help information.*/
1275 hrc = checkAndSetCommonOptions(a, pCommonOpts);
1276 if (FAILED(hrc))
1277 return RTEXITCODE_FAILURE;
1278
1279 if (strInstanceId.isNotEmpty() && strObjectName.isNotEmpty())
1280 return errorArgument(Cloud::tr("Conflicting parameters: --instance-id and --object-name can't be used together. Choose one."));
1281
1282 ComPtr<ICloudProfile> pCloudProfile = pCommonOpts->profile.pCloudProfile;
1283
1284 ComObjPtr<ICloudClient> oCloudClient;
1285 CHECK_ERROR2_RET(hrc, pCloudProfile,
1286 CreateCloudClient(oCloudClient.asOutParam()),
1287 RTEXITCODE_FAILURE);
1288 if (strInstanceId.isNotEmpty())
1289 RTPrintf(Cloud::tr("Creating cloud image with name \'%s\' from the instance \'%s\'...\n"),
1290 strDisplayName.c_str(), strInstanceId.c_str());
1291 else
1292 RTPrintf(Cloud::tr("Creating cloud image with name \'%s\' from the object \'%s\' in the bucket \'%s\'...\n"),
1293 strDisplayName.c_str(), strObjectName.c_str(), strBucketName.c_str());
1294
1295 ComPtr<IProgress> progress;
1296 CHECK_ERROR2_RET(hrc, oCloudClient,
1297 CreateImage(ComSafeArrayAsInParam(parameters), progress.asOutParam()),
1298 RTEXITCODE_FAILURE);
1299 hrc = showProgress(progress);
1300 CHECK_PROGRESS_ERROR_RET(progress, (Cloud::tr("Creating cloud image failed")), RTEXITCODE_FAILURE);
1301
1302 if (SUCCEEDED(hrc))
1303 RTPrintf(Cloud::tr("Cloud image was created successfully\n"));
1304
1305 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1306}
1307
1308
1309static RTEXITCODE exportCloudImage(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts)
1310{
1311 HRESULT hrc = S_OK;
1312
1313 static const RTGETOPTDEF s_aOptions[] =
1314 {
1315 { "--id", 'i', RTGETOPT_REQ_STRING },
1316 { "--bucket-name", 'b', RTGETOPT_REQ_STRING },
1317 { "--object-name", 'o', RTGETOPT_REQ_STRING },
1318 { "--display-name", 'd', RTGETOPT_REQ_STRING },
1319 { "--launch-mode", 'm', RTGETOPT_REQ_STRING },
1320 { "help", 1001, RTGETOPT_REQ_NOTHING },
1321 { "--help", 1002, RTGETOPT_REQ_NOTHING }
1322 };
1323 RTGETOPTSTATE GetState;
1324 RTGETOPTUNION ValueUnion;
1325 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
1326 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
1327 if (a->argc == iFirst)
1328 {
1329 RTPrintf(Cloud::tr("Empty command parameter list, show help.\n"));
1330 printHelp(g_pStdOut);
1331 return RTEXITCODE_SUCCESS;
1332 }
1333
1334 Utf8Str strImageId; /* XXX: this is vbox "image", i.e. medium */
1335 Utf8Str strBucketName;
1336 Utf8Str strObjectName;
1337 Utf8Str strDisplayName;
1338 Utf8Str strLaunchMode;
1339 com::SafeArray<BSTR> parameters;
1340
1341 int c;
1342 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
1343 {
1344 switch (c)
1345 {
1346 case 'b': /* --bucket-name */
1347 {
1348 if (strBucketName.isNotEmpty())
1349 return errorArgument(Cloud::tr("Duplicate parameter: --bucket-name"));
1350
1351 strBucketName = ValueUnion.psz;
1352 if (strBucketName.isEmpty())
1353 return errorArgument(Cloud::tr("Empty parameter: --bucket-name"));
1354
1355 break;
1356 }
1357
1358 case 'o': /* --object-name */
1359 {
1360 if (strObjectName.isNotEmpty())
1361 return errorArgument(Cloud::tr("Duplicate parameter: --object-name"));
1362
1363 strObjectName = ValueUnion.psz;
1364 if (strObjectName.isEmpty())
1365 return errorArgument(Cloud::tr("Empty parameter: --object-name"));
1366
1367 break;
1368 }
1369
1370 case 'i': /* --id */
1371 {
1372 if (strImageId.isNotEmpty())
1373 return errorArgument(Cloud::tr("Duplicate parameter: --id"));
1374
1375 strImageId = ValueUnion.psz;
1376 if (strImageId.isEmpty())
1377 return errorArgument(Cloud::tr("Empty parameter: --id"));
1378
1379 break;
1380 }
1381
1382 case 'd': /* --display-name */
1383 {
1384 if (strDisplayName.isNotEmpty())
1385 return errorArgument(Cloud::tr("Duplicate parameter: --display-name"));
1386
1387 strDisplayName = ValueUnion.psz;
1388 if (strDisplayName.isEmpty())
1389 return errorArgument(Cloud::tr("Empty parameter: --display-name"));
1390
1391 break;
1392 }
1393
1394 case 'm': /* --launch-mode */
1395 {
1396 if (strLaunchMode.isNotEmpty())
1397 return errorArgument(Cloud::tr("Duplicate parameter: --launch-mode"));
1398
1399 strLaunchMode = ValueUnion.psz;
1400 if (strLaunchMode.isEmpty())
1401 return errorArgument(Cloud::tr("Empty parameter: --launch-mode"));
1402
1403 break;
1404 }
1405
1406 case 1001:
1407 case 1002:
1408 printHelp(g_pStdOut);
1409 return RTEXITCODE_SUCCESS;
1410
1411 case VINF_GETOPT_NOT_OPTION:
1412 return errorUnknownSubcommand(ValueUnion.psz);
1413
1414 default:
1415 return errorGetOpt(c, &ValueUnion);
1416 }
1417 }
1418
1419 /* Delayed check. It allows us to print help information.*/
1420 hrc = checkAndSetCommonOptions(a, pCommonOpts);
1421 if (FAILED(hrc))
1422 return RTEXITCODE_FAILURE;
1423
1424 if (strImageId.isNotEmpty())
1425 BstrFmt("image-id=%s", strImageId.c_str()).detachTo(parameters.appendedRaw());
1426 else
1427 return errorArgument(Cloud::tr("Missing parameter: --id"));
1428
1429 if (strBucketName.isNotEmpty())
1430 BstrFmt("bucket-name=%s", strBucketName.c_str()).detachTo(parameters.appendedRaw());
1431 else
1432 return errorArgument(Cloud::tr("Missing parameter: --bucket-name"));
1433
1434 if (strObjectName.isNotEmpty())
1435 BstrFmt("object-name=%s", strObjectName.c_str()).detachTo(parameters.appendedRaw());
1436
1437 if (strDisplayName.isNotEmpty())
1438 BstrFmt("display-name=%s", strDisplayName.c_str()).detachTo(parameters.appendedRaw());
1439
1440 if (strLaunchMode.isNotEmpty())
1441 BstrFmt("launch-mode=%s", strLaunchMode.c_str()).detachTo(parameters.appendedRaw());
1442
1443
1444 ComPtr<ICloudProfile> pCloudProfile = pCommonOpts->profile.pCloudProfile;
1445
1446 ComObjPtr<ICloudClient> oCloudClient;
1447 CHECK_ERROR2_RET(hrc, pCloudProfile,
1448 CreateCloudClient(oCloudClient.asOutParam()),
1449 RTEXITCODE_FAILURE);
1450
1451 if (strObjectName.isNotEmpty())
1452 RTPrintf(Cloud::tr("Exporting image \'%s\' to the Cloud with name \'%s\'...\n"),
1453 strImageId.c_str(), strObjectName.c_str());
1454 else
1455 RTPrintf(Cloud::tr("Exporting image \'%s\' to the Cloud with default name\n"),
1456 strImageId.c_str());
1457
1458 ComPtr<IVirtualBox> pVirtualBox = a->virtualBox;
1459 SafeIfaceArray<IMedium> aImageList;
1460 CHECK_ERROR2_RET(hrc, pVirtualBox,
1461 COMGETTER(HardDisks)(ComSafeArrayAsOutParam(aImageList)),
1462 RTEXITCODE_FAILURE);
1463
1464 ComPtr<IMedium> pImage;
1465 size_t cImages = aImageList.size();
1466 bool fFound = false;
1467 for (size_t i = 0; i < cImages; ++i)
1468 {
1469 pImage = aImageList[i];
1470 Bstr bstrImageId;
1471 hrc = pImage->COMGETTER(Id)(bstrImageId.asOutParam());
1472 if (FAILED(hrc))
1473 continue;
1474
1475 com::Guid imageId(bstrImageId);
1476
1477 if (!imageId.isValid() || imageId.isZero())
1478 continue;
1479
1480 if (!strImageId.compare(imageId.toString()))
1481 {
1482 fFound = true;
1483 RTPrintf(Cloud::tr("Image %s was found\n"), strImageId.c_str());
1484 break;
1485 }
1486 }
1487
1488 if (!fFound)
1489 {
1490 RTPrintf(Cloud::tr("Process of exporting the image to the Cloud was interrupted. The image wasn't found.\n"));
1491 return RTEXITCODE_FAILURE;
1492 }
1493
1494 ComPtr<IProgress> progress;
1495 CHECK_ERROR2_RET(hrc, oCloudClient,
1496 ExportImage(pImage, ComSafeArrayAsInParam(parameters), progress.asOutParam()),
1497 RTEXITCODE_FAILURE);
1498 hrc = showProgress(progress);
1499 CHECK_PROGRESS_ERROR_RET(progress, (Cloud::tr("Export the image to the Cloud failed")), RTEXITCODE_FAILURE);
1500
1501 if (SUCCEEDED(hrc))
1502 RTPrintf(Cloud::tr("Export the image to the Cloud was successfull\n"));
1503
1504 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1505}
1506
1507static RTEXITCODE importCloudImage(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts)
1508{
1509 HRESULT hrc = S_OK;
1510
1511 static const RTGETOPTDEF s_aOptions[] =
1512 {
1513 { "--id", 'i', RTGETOPT_REQ_STRING },
1514 { "--bucket-name", 'b', RTGETOPT_REQ_STRING },
1515 { "--object-name", 'o', RTGETOPT_REQ_STRING },
1516 { "help", 1001, RTGETOPT_REQ_NOTHING },
1517 { "--help", 1002, RTGETOPT_REQ_NOTHING }
1518 };
1519 RTGETOPTSTATE GetState;
1520 RTGETOPTUNION ValueUnion;
1521 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
1522 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
1523 if (a->argc == iFirst)
1524 {
1525 RTPrintf(Cloud::tr("Empty command parameter list, show help.\n"));
1526 printHelp(g_pStdOut);
1527 return RTEXITCODE_SUCCESS;
1528 }
1529
1530 Utf8Str strImageId;
1531 Utf8Str strCompartmentId;
1532 Utf8Str strBucketName;
1533 Utf8Str strObjectName;
1534 Utf8Str strDisplayName;
1535 com::SafeArray<BSTR> parameters;
1536
1537 int c;
1538 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
1539 {
1540 switch (c)
1541 {
1542 case 'i':
1543 strImageId=ValueUnion.psz;
1544 break;
1545 case 'b':
1546 strBucketName=ValueUnion.psz;
1547 Bstr(Utf8Str("bucket-name=").append(ValueUnion.psz)).detachTo(parameters.appendedRaw());
1548 break;
1549 case 'o':
1550 strObjectName=ValueUnion.psz;
1551 Bstr(Utf8Str("object-name=").append(ValueUnion.psz)).detachTo(parameters.appendedRaw());
1552 break;
1553 case 1001:
1554 case 1002:
1555 printHelp(g_pStdOut);
1556 return RTEXITCODE_SUCCESS;
1557 case VINF_GETOPT_NOT_OPTION:
1558 return errorUnknownSubcommand(ValueUnion.psz);
1559 default:
1560 return errorGetOpt(c, &ValueUnion);
1561 }
1562 }
1563
1564 /* Delayed check. It allows us to print help information.*/
1565 hrc = checkAndSetCommonOptions(a, pCommonOpts);
1566 if (FAILED(hrc))
1567 return RTEXITCODE_FAILURE;
1568
1569 ComPtr<ICloudProfile> pCloudProfile = pCommonOpts->profile.pCloudProfile;
1570
1571 ComPtr<IVirtualBox> pVirtualBox = a->virtualBox;
1572 ComObjPtr<ICloudClient> oCloudClient;
1573 CHECK_ERROR2_RET(hrc, pCloudProfile,
1574 CreateCloudClient(oCloudClient.asOutParam()),
1575 RTEXITCODE_FAILURE);
1576 RTPrintf(Cloud::tr("Creating an object \'%s\' from the cloud image \'%s\'...\n"), strObjectName.c_str(), strImageId.c_str());
1577
1578 ComPtr<IProgress> progress;
1579 CHECK_ERROR2_RET(hrc, oCloudClient,
1580 ImportImage(Bstr(strImageId).raw(), ComSafeArrayAsInParam(parameters), progress.asOutParam()),
1581 RTEXITCODE_FAILURE);
1582 hrc = showProgress(progress);
1583 CHECK_PROGRESS_ERROR_RET(progress, (Cloud::tr("Cloud image import failed")), RTEXITCODE_FAILURE);
1584
1585 if (SUCCEEDED(hrc))
1586 {
1587 RTPrintf(Cloud::tr("Cloud image was imported successfully. Find the downloaded object with the name %s "
1588 "in the system temp folder (find the possible environment variables like TEMP, TMP and etc.)\n"),
1589 strObjectName.c_str());
1590 }
1591
1592 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1593}
1594
1595static RTEXITCODE showCloudImageInfo(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts)
1596{
1597 HRESULT hrc = S_OK;
1598
1599 static const RTGETOPTDEF s_aOptions[] =
1600 {
1601 { "--id", 'i', RTGETOPT_REQ_STRING },
1602 { "help", 1001, RTGETOPT_REQ_NOTHING },
1603 { "--help", 1002, RTGETOPT_REQ_NOTHING }
1604 };
1605 RTGETOPTSTATE GetState;
1606 RTGETOPTUNION ValueUnion;
1607 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
1608 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
1609 if (a->argc == iFirst)
1610 {
1611 RTPrintf(Cloud::tr("Empty command parameter list, show help.\n"));
1612 printHelp(g_pStdOut);
1613 return RTEXITCODE_SUCCESS;
1614 }
1615
1616 Utf8Str strImageId;
1617
1618 int c;
1619 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
1620 {
1621 switch (c)
1622 {
1623 case 'i':
1624 strImageId = ValueUnion.psz;
1625 break;
1626 case 1001:
1627 case 1002:
1628 printHelp(g_pStdOut);
1629 return RTEXITCODE_SUCCESS;
1630 case VINF_GETOPT_NOT_OPTION:
1631 return errorUnknownSubcommand(ValueUnion.psz);
1632 default:
1633 return errorGetOpt(c, &ValueUnion);
1634 }
1635 }
1636
1637 /* Delayed check. It allows us to print help information.*/
1638 hrc = checkAndSetCommonOptions(a, pCommonOpts);
1639 if (FAILED(hrc))
1640 return RTEXITCODE_FAILURE;
1641
1642 ComPtr<ICloudProfile> pCloudProfile = pCommonOpts->profile.pCloudProfile;
1643
1644 ComObjPtr<ICloudClient> oCloudClient;
1645 CHECK_ERROR2_RET(hrc, pCloudProfile,
1646 CreateCloudClient(oCloudClient.asOutParam()),
1647 RTEXITCODE_FAILURE);
1648 RTPrintf(Cloud::tr("Getting information about the cloud image with id \'%s\'...\n"), strImageId.c_str());
1649
1650 ComPtr<IStringArray> infoArray;
1651 com::SafeArray<BSTR> pStrInfoArray;
1652 ComPtr<IProgress> pProgress;
1653
1654 RTPrintf(Cloud::tr("Reply is in the form \'image property\' = \'value\'\n"));
1655 CHECK_ERROR2_RET(hrc, oCloudClient,
1656 GetImageInfo(Bstr(strImageId).raw(),
1657 infoArray.asOutParam(),
1658 pProgress.asOutParam()),
1659 RTEXITCODE_FAILURE);
1660
1661 hrc = showProgress(pProgress);
1662 CHECK_PROGRESS_ERROR_RET(pProgress, (Cloud::tr("Getting information about the cloud image failed")), RTEXITCODE_FAILURE);
1663
1664 CHECK_ERROR2_RET(hrc,
1665 infoArray, COMGETTER(Values)(ComSafeArrayAsOutParam(pStrInfoArray)),
1666 RTEXITCODE_FAILURE);
1667
1668 RTPrintf(Cloud::tr("General information about the image:\n"));
1669 size_t cParamNames = pStrInfoArray.size();
1670 for (size_t k = 0; k < cParamNames; k++)
1671 {
1672 Utf8Str data(pStrInfoArray[k]);
1673 RTPrintf("\t%s\n", data.c_str());
1674 }
1675
1676 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1677}
1678
1679static RTEXITCODE updateCloudImage(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts)
1680{
1681 RT_NOREF(a);
1682 RT_NOREF(iFirst);
1683 RT_NOREF(pCommonOpts);
1684 return RTEXITCODE_SUCCESS;
1685}
1686
1687static RTEXITCODE deleteCloudImage(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts)
1688{
1689 HRESULT hrc = S_OK;
1690
1691 static const RTGETOPTDEF s_aOptions[] =
1692 {
1693 { "--id", 'i', RTGETOPT_REQ_STRING },
1694 { "help", 1001, RTGETOPT_REQ_NOTHING },
1695 { "--help", 1002, RTGETOPT_REQ_NOTHING }
1696 };
1697 RTGETOPTSTATE GetState;
1698 RTGETOPTUNION ValueUnion;
1699 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
1700 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
1701 if (a->argc == iFirst)
1702 {
1703 RTPrintf(Cloud::tr("Empty command parameter list, show help.\n"));
1704 printHelp(g_pStdOut);
1705 return RTEXITCODE_SUCCESS;
1706 }
1707
1708 Utf8Str strImageId;
1709
1710 int c;
1711 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
1712 {
1713 switch (c)
1714 {
1715 case 'i':
1716 {
1717 if (strImageId.isNotEmpty())
1718 return errorArgument(Cloud::tr("Duplicate parameter: --id"));
1719
1720 strImageId = ValueUnion.psz;
1721 if (strImageId.isEmpty())
1722 return errorArgument(Cloud::tr("Empty parameter: --id"));
1723
1724 break;
1725 }
1726
1727 case 1001:
1728 case 1002:
1729 printHelp(g_pStdOut);
1730 return RTEXITCODE_SUCCESS;
1731 case VINF_GETOPT_NOT_OPTION:
1732 return errorUnknownSubcommand(ValueUnion.psz);
1733
1734 default:
1735 return errorGetOpt(c, &ValueUnion);
1736 }
1737 }
1738
1739 /* Delayed check. It allows us to print help information.*/
1740 hrc = checkAndSetCommonOptions(a, pCommonOpts);
1741 if (FAILED(hrc))
1742 return RTEXITCODE_FAILURE;
1743
1744 if (strImageId.isEmpty())
1745 return errorArgument(Cloud::tr("Missing parameter: --id"));
1746
1747
1748 ComPtr<ICloudProfile> pCloudProfile = pCommonOpts->profile.pCloudProfile;
1749
1750 ComObjPtr<ICloudClient> oCloudClient;
1751 CHECK_ERROR2_RET(hrc, pCloudProfile,
1752 CreateCloudClient(oCloudClient.asOutParam()),
1753 RTEXITCODE_FAILURE);
1754 RTPrintf(Cloud::tr("Deleting cloud image with id %s...\n"), strImageId.c_str());
1755
1756 ComPtr<IProgress> progress;
1757 CHECK_ERROR2_RET(hrc, oCloudClient,
1758 DeleteImage(Bstr(strImageId).raw(), progress.asOutParam()),
1759 RTEXITCODE_FAILURE);
1760 hrc = showProgress(progress);
1761 CHECK_PROGRESS_ERROR_RET(progress, (Cloud::tr("Deleting cloud image failed")), RTEXITCODE_FAILURE);
1762
1763 if (SUCCEEDED(hrc))
1764 RTPrintf(Cloud::tr("Cloud image was deleted successfully\n"));
1765
1766 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1767}
1768
1769static RTEXITCODE handleCloudImage(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts)
1770{
1771 enum
1772 {
1773 kCloudImageIota = 1000,
1774 kCloudImage_Create,
1775 kCloudImage_Delete,
1776 kCloudImage_Export,
1777 kCloudImage_Import,
1778 kCloudImage_Info,
1779 kCloudImage_Update,
1780 };
1781
1782 static const RTGETOPTDEF s_aOptions[] =
1783 {
1784 { "create", kCloudImage_Create, RTGETOPT_REQ_NOTHING },
1785 { "delete", kCloudImage_Delete, RTGETOPT_REQ_NOTHING },
1786 { "export", kCloudImage_Export, RTGETOPT_REQ_NOTHING },
1787 { "import", kCloudImage_Import, RTGETOPT_REQ_NOTHING },
1788 { "info", kCloudImage_Info, RTGETOPT_REQ_NOTHING },
1789 { "update", kCloudImage_Update, RTGETOPT_REQ_NOTHING },
1790
1791 { "help", 'h', RTGETOPT_REQ_NOTHING },
1792 { "-?", 'h', RTGETOPT_REQ_NOTHING },
1793 { "-help", 'h', RTGETOPT_REQ_NOTHING },
1794 { "--help", 'h', RTGETOPT_REQ_NOTHING },
1795 };
1796
1797 if (a->argc == iFirst)
1798 {
1799 RTPrintf(Cloud::tr("Empty command parameter list, show help.\n"));
1800 printHelp(g_pStdOut);
1801 return RTEXITCODE_SUCCESS;
1802 }
1803
1804 RTGETOPTSTATE GetState;
1805 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
1806 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
1807
1808 int c;
1809 RTGETOPTUNION ValueUnion;
1810 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
1811 {
1812 switch (c)
1813 {
1814 /* Sub-commands: */
1815 case kCloudImage_Create:
1816 setCurrentSubcommand(HELP_SCOPE_CLOUDIMAGE_CREATE);
1817 return createCloudImage(a, GetState.iNext, pCommonOpts);
1818
1819 case kCloudImage_Export:
1820 setCurrentSubcommand(HELP_SCOPE_CLOUDIMAGE_EXPORT);
1821 return exportCloudImage(a, GetState.iNext, pCommonOpts);
1822
1823 case kCloudImage_Import:
1824 setCurrentSubcommand(HELP_SCOPE_CLOUDIMAGE_IMPORT);
1825 return importCloudImage(a, GetState.iNext, pCommonOpts);
1826
1827 case kCloudImage_Info:
1828 setCurrentSubcommand(HELP_SCOPE_CLOUDIMAGE_INFO);
1829 return showCloudImageInfo(a, GetState.iNext, pCommonOpts);
1830
1831 case kCloudImage_Update:
1832// setCurrentSubcommand(HELP_SCOPE_CLOUDIMAGE_UPDATE);
1833 return updateCloudImage(a, GetState.iNext, pCommonOpts);
1834
1835 case kCloudImage_Delete:
1836 setCurrentSubcommand(HELP_SCOPE_CLOUDIMAGE_DELETE);
1837 return deleteCloudImage(a, GetState.iNext, pCommonOpts);
1838
1839 case 'h':
1840 printHelp(g_pStdOut);
1841 return RTEXITCODE_SUCCESS;
1842
1843 case VINF_GETOPT_NOT_OPTION:
1844 return errorUnknownSubcommand(ValueUnion.psz);
1845
1846 default:
1847 return errorGetOpt(c, &ValueUnion);
1848 }
1849 }
1850
1851 return errorNoSubcommand();
1852}
1853
1854#ifdef VBOX_WITH_CLOUD_NET
1855struct CloudNetworkOptions
1856{
1857 BOOL fEnable;
1858 BOOL fDisable;
1859 Bstr strNetworkId;
1860 Bstr strNetworkName;
1861};
1862typedef struct CloudNetworkOptions CLOUDNETOPT;
1863typedef CLOUDNETOPT *PCLOUDNETOPT;
1864
1865static RTEXITCODE createUpdateCloudNetworkCommon(ComPtr<ICloudNetwork> cloudNetwork, CLOUDNETOPT& options, PCLOUDCOMMONOPT pCommonOpts)
1866{
1867 HRESULT hrc = S_OK;
1868
1869 Bstr strProvider = pCommonOpts->provider.pszProviderName;
1870 Bstr strProfile = pCommonOpts->profile.pszProfileName;
1871
1872 if (options.fEnable)
1873 {
1874 CHECK_ERROR2_RET(hrc, cloudNetwork, COMSETTER(Enabled)(TRUE), RTEXITCODE_FAILURE);
1875 }
1876 if (options.fDisable)
1877 {
1878 CHECK_ERROR2_RET(hrc, cloudNetwork, COMSETTER(Enabled)(FALSE), RTEXITCODE_FAILURE);
1879 }
1880 if (options.strNetworkId.isNotEmpty())
1881 {
1882 CHECK_ERROR2_RET(hrc, cloudNetwork, COMSETTER(NetworkId)(options.strNetworkId.raw()), RTEXITCODE_FAILURE);
1883 }
1884 if (strProvider.isNotEmpty())
1885 {
1886 CHECK_ERROR2_RET(hrc, cloudNetwork, COMSETTER(Provider)(strProvider.raw()), RTEXITCODE_FAILURE);
1887 }
1888 if (strProfile.isNotEmpty())
1889 {
1890 CHECK_ERROR2_RET(hrc, cloudNetwork, COMSETTER(Profile)(strProfile.raw()), RTEXITCODE_FAILURE);
1891 }
1892
1893 return RTEXITCODE_SUCCESS;
1894}
1895
1896
1897static RTEXITCODE createCloudNetwork(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts)
1898{
1899 HRESULT hrc = S_OK;
1900 hrc = checkAndSetCommonOptions(a, pCommonOpts);
1901 if (FAILED(hrc))
1902 return RTEXITCODE_FAILURE;
1903
1904 /* Required parameters, the rest is handled in update */
1905 static const RTGETOPTDEF s_aOptions[] =
1906 {
1907 { "--disable", 'd', RTGETOPT_REQ_NOTHING },
1908 { "--enable", 'e', RTGETOPT_REQ_NOTHING },
1909 { "--network-id", 'i', RTGETOPT_REQ_STRING },
1910 { "--name", 'n', RTGETOPT_REQ_STRING },
1911 };
1912
1913 RTGETOPTSTATE GetState;
1914 RTGETOPTUNION ValueUnion;
1915 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
1916 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
1917
1918 CLOUDNETOPT options;
1919 options.fEnable = FALSE;
1920 options.fDisable = FALSE;
1921
1922 int c;
1923 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
1924 {
1925 switch (c)
1926 {
1927 case 'd':
1928 options.fDisable = TRUE;
1929 break;
1930 case 'e':
1931 options.fEnable = TRUE;
1932 break;
1933 case 'i':
1934 options.strNetworkId=ValueUnion.psz;
1935 break;
1936 case 'n':
1937 options.strNetworkName=ValueUnion.psz;
1938 break;
1939 case VINF_GETOPT_NOT_OPTION:
1940 return errorUnknownSubcommand(ValueUnion.psz);
1941 default:
1942 return errorGetOpt(c, &ValueUnion);
1943 }
1944 }
1945
1946 if (options.strNetworkName.isEmpty())
1947 return errorArgument(Cloud::tr("Missing --name parameter"));
1948 if (options.strNetworkId.isEmpty())
1949 return errorArgument(Cloud::tr("Missing --network-id parameter"));
1950
1951 ComPtr<IVirtualBox> pVirtualBox = a->virtualBox;
1952
1953 ComPtr<ICloudNetwork> cloudNetwork;
1954 CHECK_ERROR2_RET(hrc, pVirtualBox,
1955 CreateCloudNetwork(options.strNetworkName.raw(), cloudNetwork.asOutParam()),
1956 RTEXITCODE_FAILURE);
1957
1958 /* Fill out the created network */
1959 RTEXITCODE rc = createUpdateCloudNetworkCommon(cloudNetwork, options, pCommonOpts);
1960 if (RT_SUCCESS(rc))
1961 RTPrintf(Cloud::tr("Cloud network was created successfully\n"));
1962
1963 return rc;
1964}
1965
1966
1967static RTEXITCODE showCloudNetworkInfo(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts)
1968{
1969 RT_NOREF(pCommonOpts);
1970 HRESULT hrc = S_OK;
1971 static const RTGETOPTDEF s_aOptions[] =
1972 {
1973 { "--name", 'n', RTGETOPT_REQ_STRING },
1974 };
1975 RTGETOPTSTATE GetState;
1976 RTGETOPTUNION ValueUnion;
1977 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
1978 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
1979
1980 Bstr strNetworkName;
1981
1982 int c;
1983 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
1984 {
1985 switch (c)
1986 {
1987 case 'n':
1988 strNetworkName=ValueUnion.psz;
1989 break;
1990 case VINF_GETOPT_NOT_OPTION:
1991 return errorUnknownSubcommand(ValueUnion.psz);
1992 default:
1993 return errorGetOpt(c, &ValueUnion);
1994 }
1995 }
1996
1997 if (strNetworkName.isEmpty())
1998 return errorArgument(Cloud::tr("Missing --name parameter"));
1999
2000 ComPtr<IVirtualBox> pVirtualBox = a->virtualBox;
2001 ComPtr<ICloudNetwork> cloudNetwork;
2002 CHECK_ERROR2_RET(hrc, pVirtualBox,
2003 FindCloudNetworkByName(strNetworkName.raw(), cloudNetwork.asOutParam()),
2004 RTEXITCODE_FAILURE);
2005
2006 RTPrintf(Cloud::tr("Name: %ls\n"), strNetworkName.raw());
2007 BOOL fEnabled = FALSE;
2008 cloudNetwork->COMGETTER(Enabled)(&fEnabled);
2009 RTPrintf(Cloud::tr("State: %s\n"), fEnabled ? Cloud::tr("Enabled") : Cloud::tr("Disabled"));
2010 Bstr Provider;
2011 cloudNetwork->COMGETTER(Provider)(Provider.asOutParam());
2012 RTPrintf(Cloud::tr("CloudProvider: %ls\n"), Provider.raw());
2013 Bstr Profile;
2014 cloudNetwork->COMGETTER(Profile)(Profile.asOutParam());
2015 RTPrintf(Cloud::tr("CloudProfile: %ls\n"), Profile.raw());
2016 Bstr NetworkId;
2017 cloudNetwork->COMGETTER(NetworkId)(NetworkId.asOutParam());
2018 RTPrintf(Cloud::tr("CloudNetworkId: %ls\n"), NetworkId.raw());
2019 Bstr netName = BstrFmt("cloud-%ls", strNetworkName.raw());
2020 RTPrintf(Cloud::tr("VBoxNetworkName: %ls\n\n"), netName.raw());
2021
2022 return RTEXITCODE_SUCCESS;
2023}
2024
2025
2026static RTEXITCODE updateCloudNetwork(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts)
2027{
2028 HRESULT hrc = S_OK;
2029
2030 static const RTGETOPTDEF s_aOptions[] =
2031 {
2032 { "--disable", 'd', RTGETOPT_REQ_NOTHING },
2033 { "--enable", 'e', RTGETOPT_REQ_NOTHING },
2034 { "--network-id", 'i', RTGETOPT_REQ_STRING },
2035 { "--name", 'n', RTGETOPT_REQ_STRING },
2036 };
2037
2038 RTGETOPTSTATE GetState;
2039 RTGETOPTUNION ValueUnion;
2040 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
2041 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
2042
2043 CLOUDNETOPT options;
2044 options.fEnable = FALSE;
2045 options.fDisable = FALSE;
2046
2047 int c;
2048 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
2049 {
2050 switch (c)
2051 {
2052 case 'd':
2053 options.fDisable = TRUE;
2054 break;
2055 case 'e':
2056 options.fEnable = TRUE;
2057 break;
2058 case 'i':
2059 options.strNetworkId=ValueUnion.psz;
2060 break;
2061 case 'n':
2062 options.strNetworkName=ValueUnion.psz;
2063 break;
2064 case VINF_GETOPT_NOT_OPTION:
2065 return errorUnknownSubcommand(ValueUnion.psz);
2066 default:
2067 return errorGetOpt(c, &ValueUnion);
2068 }
2069 }
2070
2071 if (options.strNetworkName.isEmpty())
2072 return errorArgument(Cloud::tr("Missing --name parameter"));
2073
2074 ComPtr<IVirtualBox> pVirtualBox = a->virtualBox;
2075 ComPtr<ICloudNetwork> cloudNetwork;
2076 CHECK_ERROR2_RET(hrc, pVirtualBox,
2077 FindCloudNetworkByName(options.strNetworkName.raw(), cloudNetwork.asOutParam()),
2078 RTEXITCODE_FAILURE);
2079
2080 RTEXITCODE rc = createUpdateCloudNetworkCommon(cloudNetwork, options, pCommonOpts);
2081 if (RT_SUCCESS(rc))
2082 RTPrintf(Cloud::tr("Cloud network %ls was updated successfully\n"), options.strNetworkName.raw());
2083
2084 return rc;
2085}
2086
2087
2088static RTEXITCODE deleteCloudNetwork(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts)
2089{
2090 RT_NOREF(pCommonOpts);
2091 HRESULT hrc = S_OK;
2092 static const RTGETOPTDEF s_aOptions[] =
2093 {
2094 { "--name", 'n', RTGETOPT_REQ_STRING },
2095 };
2096 RTGETOPTSTATE GetState;
2097 RTGETOPTUNION ValueUnion;
2098 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
2099 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
2100
2101 Bstr strNetworkName;
2102
2103 int c;
2104 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
2105 {
2106 switch (c)
2107 {
2108 case 'n':
2109 strNetworkName=ValueUnion.psz;
2110 break;
2111 case VINF_GETOPT_NOT_OPTION:
2112 return errorUnknownSubcommand(ValueUnion.psz);
2113 default:
2114 return errorGetOpt(c, &ValueUnion);
2115 }
2116 }
2117
2118 if (strNetworkName.isEmpty())
2119 return errorArgument(Cloud::tr("Missing --name parameter"));
2120
2121 ComPtr<IVirtualBox> pVirtualBox = a->virtualBox;
2122 ComPtr<ICloudNetwork> cloudNetwork;
2123 CHECK_ERROR2_RET(hrc, pVirtualBox,
2124 FindCloudNetworkByName(strNetworkName.raw(), cloudNetwork.asOutParam()),
2125 RTEXITCODE_FAILURE);
2126
2127 CHECK_ERROR2_RET(hrc, pVirtualBox,
2128 RemoveCloudNetwork(cloudNetwork),
2129 RTEXITCODE_FAILURE);
2130
2131 if (SUCCEEDED(hrc))
2132 RTPrintf(Cloud::tr("Cloud network %ls was deleted successfully\n"), strNetworkName.raw());
2133
2134 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2135}
2136
2137
2138static bool errorOccured(HRESULT hrc, const char *pszFormat, ...)
2139{
2140 if (FAILED(hrc))
2141 {
2142 va_list va;
2143 va_start(va, pszFormat);
2144 Utf8Str strError(pszFormat, va);
2145 va_end(va);
2146 RTStrmPrintf(g_pStdErr, "%s (rc=%x)\n", strError.c_str(), hrc);
2147 RTStrmFlush(g_pStdErr);
2148 return true;
2149 }
2150 return false;
2151}
2152
2153
2154static int composeTemplatePath(const char *pcszTemplate, Bstr& strFullPath)
2155{
2156 com::Utf8Str strTemplatePath;
2157 int rc = RTPathAppPrivateNoArchCxx(strTemplatePath);
2158 if (RT_SUCCESS(rc))
2159 rc = RTPathAppendCxx(strTemplatePath, "UnattendedTemplates");
2160 if (RT_SUCCESS(rc))
2161 rc = RTPathAppendCxx(strTemplatePath, pcszTemplate);
2162 if (RT_FAILURE(rc))
2163 {
2164 RTStrmPrintf(g_pStdErr, Cloud::tr("Failed to compose path to the unattended installer script templates (%Rrc)"), rc);
2165 RTStrmFlush(g_pStdErr);
2166 }
2167 else
2168 strFullPath = strTemplatePath;
2169
2170 return rc;
2171}
2172
2173/**
2174 * @returns COM status code.
2175 * @retval S_OK if url needs proxy.
2176 * @retval S_FALSE if noproxy for the URL.
2177 */
2178static HRESULT getSystemProxyForUrl(const com::Utf8Str &strUrl, Bstr &strProxy)
2179{
2180 /** @todo r=bird: LogRel is pointless here. */
2181#ifndef VBOX_WITH_PROXY_INFO
2182 RT_NOREF(strUrl, strProxy);
2183 LogRel(("CLOUD-NET: Proxy support is disabled. Using direct connection.\n"));
2184 return S_FALSE;
2185#else /* VBOX_WITH_PROXY_INFO */
2186 HRESULT hrc = E_FAIL;
2187 RTHTTP hHttp;
2188 int rc = RTHttpCreate(&hHttp);
2189 if (RT_SUCCESS(rc))
2190 {
2191 rc = RTHttpUseSystemProxySettings(hHttp);
2192 if (RT_SUCCESS(rc))
2193 {
2194 RTHTTPPROXYINFO proxy;
2195 rc = RTHttpQueryProxyInfoForUrl(hHttp, strUrl.c_str(), &proxy);
2196 if (RT_SUCCESS(rc))
2197 {
2198 const char *pcszProxyScheme = "";
2199 switch (proxy.enmProxyType)
2200 {
2201 case RTHTTPPROXYTYPE_NOPROXY:
2202 pcszProxyScheme = NULL;
2203 hrc = S_FALSE;
2204 break;
2205 case RTHTTPPROXYTYPE_HTTP:
2206 pcszProxyScheme = "http://";
2207 break;
2208 case RTHTTPPROXYTYPE_HTTPS:
2209 pcszProxyScheme = "https://";
2210 break;
2211 case RTHTTPPROXYTYPE_SOCKS4:
2212 pcszProxyScheme = "socks4://";
2213 break;
2214 case RTHTTPPROXYTYPE_SOCKS5:
2215 pcszProxyScheme = "socks://";
2216 break;
2217 case RTHTTPPROXYTYPE_INVALID:
2218 case RTHTTPPROXYTYPE_UNKNOWN:
2219 case RTHTTPPROXYTYPE_END:
2220 case RTHTTPPROXYTYPE_32BIT_HACK:
2221 break;
2222 }
2223 if (pcszProxyScheme && *pcszProxyScheme != '\0')
2224 {
2225 if (proxy.pszProxyUsername || proxy.pszProxyPassword)
2226 LogRel(("CLOUD-NET: Warning! Code doesn't yet handle proxy user or password. Sorry.\n"));
2227 if (proxy.uProxyPort != UINT32_MAX)
2228 strProxy.printf("%s%s:%d", pcszProxyScheme, proxy.pszProxyHost, proxy.uProxyPort);
2229 else
2230 strProxy.printf("%s%s", pcszProxyScheme, proxy.pszProxyHost);
2231 hrc = S_OK;
2232 }
2233 else if (pcszProxyScheme)
2234 {
2235 LogRel(("CLOUD-NET: Unknown proxy type %d. Using direct connection.\n", proxy.enmProxyType));
2236 AssertFailed();
2237 }
2238 RTHttpFreeProxyInfo(&proxy);
2239 }
2240 else
2241 LogRel(("CLOUD-NET: Failed to get proxy for %s (rc=%Rrc)\n", strUrl.c_str(), rc));
2242 }
2243 else
2244 LogRel(("CLOUD-NET: Failed to use system proxy (rc=%Rrc)\n", rc));
2245 RTHttpDestroy(hHttp);
2246 }
2247 else
2248 LogRel(("CLOUD-NET: Failed to create HTTP context (rc=%Rrc)\n", rc));
2249 return hrc;
2250#endif /* VBOX_WITH_PROXY_INFO */
2251}
2252
2253static HRESULT localGatewayImagePath(const Bstr &aDefaultMachineFolder, Bstr &aLgwImage)
2254{
2255 com::Utf8Str strPath(aDefaultMachineFolder);
2256 int rc = RTPathAppendCxx(strPath, "gateways");
2257 if (RT_SUCCESS(rc))
2258 rc = RTPathAppendCxx(strPath, "lgw.vdi");
2259 if (RT_SUCCESS(rc))
2260 aLgwImage = strPath;
2261
2262 return rc;
2263}
2264
2265static HRESULT createLocalGatewayImage(ComPtr<IVirtualBox> virtualBox, const Bstr& aGatewayIso, const Bstr& aGuestAdditionsIso, const Bstr& aProxy)
2266{
2267 /* Check if the image already exists. */
2268 HRESULT hrc;
2269
2270 Bstr strGatewayVM = "lgw";
2271 Bstr strUser = "vbox";
2272 Bstr strPassword = "vbox";
2273
2274 Bstr strInstallerScript;
2275 Bstr strPostInstallScript;
2276
2277 if (RT_FAILURE(composeTemplatePath("lgw_ks.cfg", strInstallerScript)))
2278 return E_FAIL;
2279 if (RT_FAILURE(composeTemplatePath("lgw_postinstall.sh", strPostInstallScript)))
2280 return E_FAIL;
2281
2282 ComPtr<ISystemProperties> systemProperties;
2283 ProxyMode_T enmProxyMode;
2284 Bstr strProxy;
2285 ComPtr<IMedium> hd;
2286 Bstr defaultMachineFolder;
2287 Bstr guestAdditionsISO;
2288 hrc = virtualBox->COMGETTER(SystemProperties)(systemProperties.asOutParam());
2289 if (errorOccured(hrc, Cloud::tr("Failed to obtain system properties.")))
2290 return hrc;
2291 if (aProxy.isNotEmpty())
2292 strProxy = aProxy;
2293 else
2294 {
2295 hrc = systemProperties->COMGETTER(ProxyMode)(&enmProxyMode);
2296 if (errorOccured(hrc, Cloud::tr("Failed to obtain proxy mode.")))
2297 return hrc;
2298 switch (enmProxyMode)
2299 {
2300 case ProxyMode_NoProxy:
2301 strProxy.setNull();
2302 break;
2303 case ProxyMode_Manual:
2304 hrc = systemProperties->COMGETTER(ProxyURL)(strProxy.asOutParam());
2305 if (errorOccured(hrc, Cloud::tr("Failed to obtain proxy URL.")))
2306 return hrc;
2307 break;
2308 case ProxyMode_System:
2309 hrc = getSystemProxyForUrl("https://dl.fedoraproject.org", strProxy);
2310 if (FAILED(hrc))
2311 errorOccured(hrc, Cloud::tr("Failed to get system proxy for https://dl.fedoraproject.org. Will use direct connection."));
2312 break;
2313 default: /* To get rid of ProxyMode_32BitHack 'warning' */
2314 AssertFailed();
2315 break;
2316 }
2317
2318 }
2319 hrc = systemProperties->COMGETTER(DefaultMachineFolder)(defaultMachineFolder.asOutParam());
2320 if (errorOccured(hrc, Cloud::tr("Failed to obtain default machine folder.")))
2321 return hrc;
2322 if (aGuestAdditionsIso.isEmpty())
2323 {
2324 hrc = systemProperties->COMGETTER(DefaultAdditionsISO)(guestAdditionsISO.asOutParam());
2325 if (errorOccured(hrc, Cloud::tr("Failed to obtain default guest additions ISO path.")))
2326 return hrc;
2327 if (guestAdditionsISO.isEmpty())
2328 {
2329 errorOccured(E_INVALIDARG, Cloud::tr("The default guest additions ISO path is empty nor it is provided as --guest-additions-iso parameter. Cannot proceed without it."));
2330 return E_INVALIDARG;
2331 }
2332 }
2333 else
2334 guestAdditionsISO = aGuestAdditionsIso;
2335
2336 Bstr strGatewayImage;
2337 int rc = localGatewayImagePath(defaultMachineFolder, strGatewayImage);
2338 if (RT_FAILURE(rc))
2339 {
2340 RTStrmPrintf(g_pStdErr, Cloud::tr("Failed to compose a path to the local gateway image (%Rrc)"), rc);
2341 RTStrmFlush(g_pStdErr);
2342 return E_FAIL;
2343 }
2344 hrc = virtualBox->OpenMedium(strGatewayImage.raw(), DeviceType_HardDisk, AccessMode_ReadWrite, FALSE, hd.asOutParam());
2345 /* If the image is already in place, there is nothing for us to do. */
2346 if (SUCCEEDED(hrc))
2347 {
2348 RTPrintf(Cloud::tr("Local gateway image already exists, skipping image preparation step.\n"));
2349 return hrc;
2350 }
2351
2352 RTPrintf(Cloud::tr("Preparing unattended install of temporary local gateway machine from %ls...\n"), aGatewayIso.raw());
2353 /* The image does not exist, let's try to open the provided ISO file. */
2354 ComPtr<IMedium> iso;
2355 hrc = virtualBox->OpenMedium(aGatewayIso.raw(), DeviceType_DVD, AccessMode_ReadOnly, FALSE, iso.asOutParam());
2356 if (errorOccured(hrc, Cloud::tr("Failed to open %ls."), aGatewayIso.raw()))
2357 return hrc;
2358
2359 ComPtr<IMachine> machine;
2360 SafeArray<IN_BSTR> groups;
2361 groups.push_back(Bstr("/gateways").mutableRaw());
2362 hrc = virtualBox->CreateMachine(NULL, strGatewayVM.raw(), ComSafeArrayAsInParam(groups), Bstr("Oracle_64").raw(), Bstr("").raw(), machine.asOutParam());
2363 if (errorOccured(hrc, Cloud::tr("Failed to create '%ls'."), strGatewayVM.raw()))
2364 return hrc;
2365 /* Initial configuration */
2366 hrc = machine->ApplyDefaults(NULL);
2367 if (errorOccured(hrc, Cloud::tr("Failed to apply defaults to '%ls'."), strGatewayVM.raw()))
2368 return hrc;
2369
2370 hrc = machine->COMSETTER(CPUCount)(2);
2371 if (errorOccured(hrc, Cloud::tr("Failed to adjust CPU count for '%ls'."), strGatewayVM.raw()))
2372 return hrc;
2373
2374 hrc = machine->COMSETTER(MemorySize)(512/*MB*/);
2375 if (errorOccured(hrc, Cloud::tr("Failed to adjust memory size for '%ls'."), strGatewayVM.raw()))
2376 return hrc;
2377
2378 /* No need for audio -- disable it. */
2379 ComPtr<IAudioAdapter> audioAdapter;
2380 hrc = machine->COMGETTER(AudioAdapter)(audioAdapter.asOutParam());
2381 if (errorOccured(hrc, Cloud::tr("Failed to set attachment type for the second network adapter.")))
2382 return hrc;
2383
2384 hrc = audioAdapter->COMSETTER(Enabled)(FALSE);
2385 if (errorOccured(hrc, Cloud::tr("Failed to disable the audio adapter.")))
2386 return hrc;
2387 audioAdapter.setNull();
2388
2389 hrc = virtualBox->RegisterMachine(machine);
2390 if (errorOccured(hrc, Cloud::tr("Failed to register '%ls'."), strGatewayVM.raw()))
2391 return hrc;
2392
2393 hrc = virtualBox->CreateMedium(Bstr("VDI").raw(), strGatewayImage.raw(), AccessMode_ReadWrite, DeviceType_HardDisk, hd.asOutParam());
2394 if (errorOccured(hrc, Cloud::tr("Failed to create %ls."), strGatewayImage.raw()))
2395 return hrc;
2396
2397 ComPtr<IProgress> progress;
2398 com::SafeArray<MediumVariant_T> mediumVariant;
2399 mediumVariant.push_back(MediumVariant_Standard);
2400
2401 /* Kick off the creation of a dynamic growing disk image with the given capacity. */
2402 hrc = hd->CreateBaseStorage(8ll * 1000 * 1000 * 1000 /* 8GB */,
2403 ComSafeArrayAsInParam(mediumVariant),
2404 progress.asOutParam());
2405 if (errorOccured(hrc, Cloud::tr("Failed to create base storage for local gateway image.")))
2406 return hrc;
2407
2408 hrc = showProgress(progress);
2409 CHECK_PROGRESS_ERROR_RET(progress, (Cloud::tr("Failed to create base storage for local gateway image.")), hrc);
2410
2411 ComPtr<ISession> session;
2412 hrc = session.createInprocObject(CLSID_Session);
2413 hrc = machine->LockMachine(session, LockType_Write);
2414 if (errorOccured(hrc, Cloud::tr("Failed to lock '%ls' for modifications."), strGatewayVM.raw()))
2415 return hrc;
2416
2417 ComPtr<IMachine> sessionMachine;
2418 hrc = session->COMGETTER(Machine)(sessionMachine.asOutParam());
2419 if (errorOccured(hrc, Cloud::tr("Failed to obtain a mutable machine.")))
2420 return hrc;
2421
2422 hrc = sessionMachine->AttachDevice(Bstr("SATA").raw(), 0, 0, DeviceType_HardDisk, hd);
2423 if (errorOccured(hrc, Cloud::tr("Failed to attach HD to '%ls'."), strGatewayVM.raw()))
2424 return hrc;
2425
2426 hrc = sessionMachine->AttachDevice(Bstr("IDE").raw(), 0, 0, DeviceType_DVD, iso);
2427 if (errorOccured(hrc, Cloud::tr("Failed to attach ISO to '%ls'."), strGatewayVM.raw()))
2428 return hrc;
2429
2430 /* Save settings */
2431 hrc = sessionMachine->SaveSettings();
2432 if (errorOccured(hrc, Cloud::tr("Failed to save '%ls' settings."), strGatewayVM.raw()))
2433 return hrc;
2434 session->UnlockMachine();
2435
2436 /* Prepare unattended install */
2437 ComPtr<IUnattended> unattended;
2438 hrc = virtualBox->CreateUnattendedInstaller(unattended.asOutParam());
2439 if (errorOccured(hrc, Cloud::tr("Failed to create unattended installer.")))
2440 return hrc;
2441
2442 hrc = unattended->COMSETTER(Machine)(machine);
2443 if (errorOccured(hrc, Cloud::tr("Failed to set machine for the unattended installer.")))
2444 return hrc;
2445
2446 hrc = unattended->COMSETTER(IsoPath)(aGatewayIso.raw());
2447 if (errorOccured(hrc, Cloud::tr("Failed to set machine for the unattended installer.")))
2448 return hrc;
2449
2450 hrc = unattended->COMSETTER(User)(strUser.raw());
2451 if (errorOccured(hrc, Cloud::tr("Failed to set user for the unattended installer.")))
2452 return hrc;
2453
2454 hrc = unattended->COMSETTER(Password)(strPassword.raw());
2455 if (errorOccured(hrc, Cloud::tr("Failed to set password for the unattended installer.")))
2456 return hrc;
2457
2458 hrc = unattended->COMSETTER(FullUserName)(strUser.raw());
2459 if (errorOccured(hrc, Cloud::tr("Failed to set full user name for the unattended installer.")))
2460 return hrc;
2461
2462 hrc = unattended->COMSETTER(InstallGuestAdditions)(TRUE);
2463 if (errorOccured(hrc, Cloud::tr("Failed to enable guest addtions for the unattended installer.")))
2464 return hrc;
2465
2466 hrc = unattended->COMSETTER(AdditionsIsoPath)(guestAdditionsISO.raw());
2467 if (errorOccured(hrc, Cloud::tr("Failed to set guest addtions ISO path for the unattended installer.")))
2468 return hrc;
2469
2470 hrc = unattended->COMSETTER(ScriptTemplatePath)(strInstallerScript.raw());
2471 if (errorOccured(hrc, Cloud::tr("Failed to set script template for the unattended installer.")))
2472 return hrc;
2473
2474 hrc = unattended->COMSETTER(PostInstallScriptTemplatePath)(strPostInstallScript.raw());
2475 if (errorOccured(hrc, Cloud::tr("Failed to set post install script template for the unattended installer.")))
2476 return hrc;
2477
2478 if (strProxy.isNotEmpty())
2479 {
2480 hrc = unattended->COMSETTER(Proxy)(strProxy.raw());
2481 if (errorOccured(hrc, Cloud::tr("Failed to set post install script template for the unattended installer.")))
2482 return hrc;
2483 }
2484
2485 hrc = unattended->Prepare();
2486 if (errorOccured(hrc, Cloud::tr("Failed to prepare unattended installation.")))
2487 return hrc;
2488
2489 hrc = unattended->ConstructMedia();
2490 if (errorOccured(hrc, Cloud::tr("Failed to construct media for unattended installation.")))
2491 return hrc;
2492
2493 hrc = unattended->ReconfigureVM();
2494 if (errorOccured(hrc, Cloud::tr("Failed to reconfigure %ls for unattended installation."), strGatewayVM.raw()))
2495 return hrc;
2496
2497#define SHOW_ATTR(a_Attr, a_szText, a_Type, a_szFmt) do { \
2498 a_Type Value; \
2499 HRESULT hrc2 = unattended->COMGETTER(a_Attr)(&Value); \
2500 if (SUCCEEDED(hrc2)) \
2501 RTPrintf(" %32s = " a_szFmt "\n", a_szText, Value); \
2502 else \
2503 RTPrintf(Cloud::tr(" %32s = failed: %Rhrc\n"), a_szText, hrc2); \
2504 } while (0)
2505#define SHOW_STR_ATTR(a_Attr, a_szText) do { \
2506 Bstr bstrString; \
2507 HRESULT hrc2 = unattended->COMGETTER(a_Attr)(bstrString.asOutParam()); \
2508 if (SUCCEEDED(hrc2)) \
2509 RTPrintf(" %32s = %ls\n", a_szText, bstrString.raw()); \
2510 else \
2511 RTPrintf(Cloud::tr(" %32s = failed: %Rhrc\n"), a_szText, hrc2); \
2512 } while (0)
2513
2514 SHOW_STR_ATTR(IsoPath, "isoPath");
2515 SHOW_STR_ATTR(User, "user");
2516 SHOW_STR_ATTR(Password, "password");
2517 SHOW_STR_ATTR(FullUserName, "fullUserName");
2518 SHOW_STR_ATTR(ProductKey, "productKey");
2519 SHOW_STR_ATTR(AdditionsIsoPath, "additionsIsoPath");
2520 SHOW_ATTR( InstallGuestAdditions, "installGuestAdditions", BOOL, "%RTbool");
2521 SHOW_STR_ATTR(ValidationKitIsoPath, "validationKitIsoPath");
2522 SHOW_ATTR( InstallTestExecService, "installTestExecService", BOOL, "%RTbool");
2523 SHOW_STR_ATTR(Locale, "locale");
2524 SHOW_STR_ATTR(Country, "country");
2525 SHOW_STR_ATTR(TimeZone, "timeZone");
2526 SHOW_STR_ATTR(Proxy, "proxy");
2527 SHOW_STR_ATTR(Hostname, "hostname");
2528 SHOW_STR_ATTR(PackageSelectionAdjustments, "packageSelectionAdjustments");
2529 SHOW_STR_ATTR(AuxiliaryBasePath, "auxiliaryBasePath");
2530 SHOW_ATTR( ImageIndex, "imageIndex", ULONG, "%u");
2531 SHOW_STR_ATTR(ScriptTemplatePath, "scriptTemplatePath");
2532 SHOW_STR_ATTR(PostInstallScriptTemplatePath, "postInstallScriptTemplatePath");
2533 SHOW_STR_ATTR(PostInstallCommand, "postInstallCommand");
2534 SHOW_STR_ATTR(ExtraInstallKernelParameters, "extraInstallKernelParameters");
2535 SHOW_STR_ATTR(Language, "language");
2536 SHOW_STR_ATTR(DetectedOSTypeId, "detectedOSTypeId");
2537 SHOW_STR_ATTR(DetectedOSVersion, "detectedOSVersion");
2538 SHOW_STR_ATTR(DetectedOSFlavor, "detectedOSFlavor");
2539 SHOW_STR_ATTR(DetectedOSLanguages, "detectedOSLanguages");
2540 SHOW_STR_ATTR(DetectedOSHints, "detectedOSHints");
2541
2542#undef SHOW_STR_ATTR
2543#undef SHOW_ATTR
2544
2545 /* 'unattended' is no longer needed. */
2546 unattended.setNull();
2547
2548 RTPrintf(Cloud::tr("Performing unattended install of temporary local gateway...\n"));
2549
2550 hrc = machine->LaunchVMProcess(session, Bstr("gui").raw(), ComSafeArrayNullInParam(), progress.asOutParam());
2551 if (errorOccured(hrc, Cloud::tr("Failed to launch '%ls'."), strGatewayVM.raw()))
2552 return hrc;
2553
2554 hrc = progress->WaitForCompletion(-1);
2555 if (errorOccured(hrc, Cloud::tr("Failed to launch '%ls'."), strGatewayVM.raw()))
2556 return hrc;
2557
2558 unsigned i = 0;
2559 const char progressChars[] = { '|', '/', '-', '\\'};
2560 MachineState_T machineState;
2561 uint64_t u64Started = RTTimeMilliTS();
2562 do
2563 {
2564 RTThreadSleep(1000); /* One second */
2565 hrc = machine->COMGETTER(State)(&machineState);
2566 if (errorOccured(hrc, Cloud::tr("Failed to get machine state.")))
2567 break;
2568 RTPrintf("\r%c", progressChars[i++ % sizeof(progressChars)]);
2569 if (machineState == MachineState_Aborted)
2570 {
2571 errorOccured(E_ABORT, Cloud::tr("Temporary local gateway VM has aborted."));
2572 return E_ABORT;
2573 }
2574 }
2575 while (machineState != MachineState_PoweredOff && RTTimeMilliTS() - u64Started < 40 * 60 * 1000);
2576
2577 if (machineState != MachineState_PoweredOff)
2578 {
2579 errorOccured(E_ABORT, Cloud::tr("Timed out (40min) while waiting for unattended install to finish."));
2580 return E_ABORT;
2581 }
2582 /* Machine will still be immutable for a short while after powering off, let's wait a little. */
2583 RTThreadSleep(5000); /* Five seconds */
2584
2585 RTPrintf(Cloud::tr("\rDone.\n"));
2586
2587 hrc = machine->LockMachine(session, LockType_Write);
2588 if (errorOccured(hrc, Cloud::tr("Failed to lock '%ls' for modifications."), strGatewayVM.raw()))
2589 return hrc;
2590
2591 RTPrintf(Cloud::tr("Detaching local gateway image...\n"));
2592 hrc = session->COMGETTER(Machine)(sessionMachine.asOutParam());
2593 if (errorOccured(hrc, Cloud::tr("Failed to obtain a mutable machine.")))
2594 return hrc;
2595
2596 hrc = sessionMachine->DetachDevice(Bstr("SATA").raw(), 0, 0);
2597 if (errorOccured(hrc, Cloud::tr("Failed to detach HD to '%ls'."), strGatewayVM.raw()))
2598 return hrc;
2599
2600 /* Remove the image from the media registry. */
2601 hd->Close();
2602
2603 /* Save settings */
2604 hrc = sessionMachine->SaveSettings();
2605 if (errorOccured(hrc, Cloud::tr("Failed to save '%ls' settings."), strGatewayVM.raw()))
2606 return hrc;
2607 session->UnlockMachine();
2608
2609#if 0
2610 /** @todo Unregistering the temporary VM makes the image mutable again. Find out the way around it! */
2611 RTPrintf(Cloud::tr("Unregistering temporary local gateway machine...\n"));
2612 SafeIfaceArray<IMedium> media;
2613 hrc = machine->Unregister(CleanupMode_DetachAllReturnNone, ComSafeArrayAsOutParam(media));
2614 if (errorOccured(hrc, Cloud::tr("Failed to unregister '%ls'."), strGatewayVM.raw()))
2615 return hrc;
2616 hrc = machine->DeleteConfig(ComSafeArrayAsInParam(media), progress.asOutParam());
2617 if (errorOccured(hrc, Cloud::tr("Failed to delete config for '%ls'."), strGatewayVM.raw()))
2618 return hrc;
2619 hrc = progress->WaitForCompletion(-1);
2620 if (errorOccured(hrc, Cloud::tr("Failed to delete config for '%ls'."), strGatewayVM.raw()))
2621 return hrc;
2622#endif
2623
2624 RTPrintf(Cloud::tr("Making local gateway image immutable...\n"));
2625 hrc = virtualBox->OpenMedium(strGatewayImage.raw(), DeviceType_HardDisk, AccessMode_ReadWrite, FALSE, hd.asOutParam());
2626 if (errorOccured(hrc, Cloud::tr("Failed to open '%ls'."), strGatewayImage.raw()))
2627 return hrc;
2628 hd->COMSETTER(Type)(MediumType_Immutable);
2629 if (errorOccured(hrc, Cloud::tr("Failed to make '%ls' immutable."), strGatewayImage.raw()))
2630 return hrc;
2631
2632 return S_OK;
2633}
2634
2635
2636static RTEXITCODE setupCloudNetworkEnv(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts)
2637{
2638 RT_NOREF(pCommonOpts);
2639 HRESULT hrc = S_OK;
2640 static const RTGETOPTDEF s_aOptions[] =
2641 {
2642 { "--gateway-os-name", 'n', RTGETOPT_REQ_STRING },
2643 { "--gateway-os-version", 'v', RTGETOPT_REQ_STRING },
2644 { "--gateway-shape", 's', RTGETOPT_REQ_STRING },
2645 { "--tunnel-network-name", 't', RTGETOPT_REQ_STRING },
2646 { "--tunnel-network-range", 'r', RTGETOPT_REQ_STRING },
2647 { "--guest-additions-iso", 'a', RTGETOPT_REQ_STRING },
2648 { "--local-gateway-iso", 'l', RTGETOPT_REQ_STRING },
2649 { "--proxy", 'p', RTGETOPT_REQ_STRING },
2650 { "--compartment-id", 'c', RTGETOPT_REQ_STRING }
2651 };
2652 RTGETOPTSTATE GetState;
2653 RTGETOPTUNION ValueUnion;
2654 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
2655 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
2656
2657 Bstr strGatewayOsName;
2658 Bstr strGatewayOsVersion;
2659 Bstr strGatewayShape;
2660 Bstr strTunnelNetworkName;
2661 Bstr strTunnelNetworkRange;
2662 Bstr strLocalGatewayIso;
2663 Bstr strGuestAdditionsIso;
2664 Bstr strProxy;
2665 Bstr strCompartmentId;
2666
2667 int c;
2668 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
2669 {
2670 switch (c)
2671 {
2672 case 'n':
2673 strGatewayOsName=ValueUnion.psz;
2674 break;
2675 case 'v':
2676 strGatewayOsVersion=ValueUnion.psz;
2677 break;
2678 case 's':
2679 strGatewayShape=ValueUnion.psz;
2680 break;
2681 case 't':
2682 strTunnelNetworkName=ValueUnion.psz;
2683 break;
2684 case 'r':
2685 strTunnelNetworkRange=ValueUnion.psz;
2686 break;
2687 case 'l':
2688 strLocalGatewayIso=ValueUnion.psz;
2689 break;
2690 case 'a':
2691 strGuestAdditionsIso=ValueUnion.psz;
2692 break;
2693 case 'p':
2694 strProxy=ValueUnion.psz;
2695 break;
2696 case 'c':
2697 strCompartmentId=ValueUnion.psz;
2698 break;
2699 case VINF_GETOPT_NOT_OPTION:
2700 return errorUnknownSubcommand(ValueUnion.psz);
2701 default:
2702 return errorGetOpt(c, &ValueUnion);
2703 }
2704 }
2705
2706 /* Delayed check. It allows us to print help information.*/
2707 hrc = checkAndSetCommonOptions(a, pCommonOpts);
2708 if (FAILED(hrc))
2709 return RTEXITCODE_FAILURE;
2710
2711 if (strLocalGatewayIso.isEmpty())
2712 return errorArgument(Cloud::tr("Missing --local-gateway-iso parameter"));
2713
2714 ComPtr<IVirtualBox> pVirtualBox = a->virtualBox;
2715
2716 hrc = createLocalGatewayImage(pVirtualBox, strLocalGatewayIso, strGuestAdditionsIso, strProxy);
2717 if (FAILED(hrc))
2718 return RTEXITCODE_FAILURE;
2719
2720 RTPrintf(Cloud::tr("Setting up tunnel network in the cloud...\n"));
2721
2722 ComPtr<ICloudProfile> pCloudProfile = pCommonOpts->profile.pCloudProfile;
2723
2724 /* Use user-specified profile instead of default one. */
2725 if (strCompartmentId.isNotEmpty())
2726 {
2727 CHECK_ERROR2_RET(hrc, pCloudProfile,
2728 SetProperty(Bstr("compartment").raw(), Bstr(strCompartmentId).raw()),
2729 RTEXITCODE_FAILURE);
2730 }
2731
2732 ComObjPtr<ICloudClient> oCloudClient;
2733 CHECK_ERROR2_RET(hrc, pCloudProfile,
2734 CreateCloudClient(oCloudClient.asOutParam()),
2735 RTEXITCODE_FAILURE);
2736
2737 ComPtr<ICloudNetworkEnvironmentInfo> cloudNetworkEnv;
2738 ComPtr<IProgress> progress;
2739 CHECK_ERROR2_RET(hrc, oCloudClient,
2740 SetupCloudNetworkEnvironment(strTunnelNetworkName.raw(), strTunnelNetworkRange.raw(),
2741 strGatewayOsName.raw(), strGatewayOsVersion.raw(), strGatewayShape.raw(),
2742 cloudNetworkEnv.asOutParam(), progress.asOutParam()),
2743 RTEXITCODE_FAILURE);
2744
2745 hrc = showProgress(progress);
2746 CHECK_PROGRESS_ERROR_RET(progress, (Cloud::tr("Setting up cloud network environment failed")), RTEXITCODE_FAILURE);
2747
2748 Bstr tunnelNetworkId;
2749 hrc = cloudNetworkEnv->COMGETTER(TunnelNetworkId)(tunnelNetworkId.asOutParam());
2750 RTPrintf(Cloud::tr("Cloud network environment was set up successfully. Tunnel network id is: %ls\n"), tunnelNetworkId.raw());
2751
2752 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2753}
2754
2755
2756static RTEXITCODE handleCloudNetwork(HandlerArg *a, int iFirst, PCLOUDCOMMONOPT pCommonOpts)
2757{
2758 enum
2759 {
2760 kCloudNetworkIota = 1000,
2761 kCloudNetwork_Create,
2762 kCloudNetwork_Delete,
2763 kCloudNetwork_Info,
2764 kCloudNetwork_Setup,
2765 kCloudNetwork_Update,
2766 };
2767
2768 static const RTGETOPTDEF s_aOptions[] =
2769 {
2770 { "create", kCloudNetwork_Create, RTGETOPT_REQ_NOTHING },
2771 { "delete", kCloudNetwork_Delete, RTGETOPT_REQ_NOTHING },
2772 { "info", kCloudNetwork_Info, RTGETOPT_REQ_NOTHING },
2773 { "setup", kCloudNetwork_Setup, RTGETOPT_REQ_NOTHING },
2774 { "update", kCloudNetwork_Update, RTGETOPT_REQ_NOTHING },
2775 };
2776
2777 if (a->argc < 1)
2778 return errorNoSubcommand();
2779
2780 RTGETOPTSTATE GetState;
2781 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
2782 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
2783
2784 int c;
2785 RTGETOPTUNION ValueUnion;
2786 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
2787 {
2788 switch (c)
2789 {
2790 /* Sub-commands: */
2791 case kCloudNetwork_Create:
2792 return createCloudNetwork(a, GetState.iNext, pCommonOpts);
2793
2794 case kCloudNetwork_Info:
2795 return showCloudNetworkInfo(a, GetState.iNext, pCommonOpts);
2796
2797 case kCloudNetwork_Update:
2798 return updateCloudNetwork(a, GetState.iNext, pCommonOpts);
2799
2800 case kCloudNetwork_Delete:
2801 return deleteCloudNetwork(a, GetState.iNext, pCommonOpts);
2802
2803 case kCloudNetwork_Setup:
2804 return setupCloudNetworkEnv(a, GetState.iNext, pCommonOpts);
2805
2806 case VINF_GETOPT_NOT_OPTION:
2807 return errorUnknownSubcommand(ValueUnion.psz);
2808
2809 default:
2810 return errorGetOpt(c, &ValueUnion);
2811 }
2812 }
2813
2814 return errorNoSubcommand();
2815}
2816#endif /* VBOX_WITH_CLOUD_NET */
2817
2818
2819RTEXITCODE handleCloud(HandlerArg *a)
2820{
2821 enum
2822 {
2823 kCloudIota = 1000,
2824 kCloud_Image,
2825 kCloud_Instance,
2826 kCloud_List,
2827 kCloud_Machine,
2828 kCloud_Network,
2829 kCloud_Object,
2830 kCloud_ShowVMInfo,
2831 kCloud_Volume,
2832 };
2833
2834 static const RTGETOPTDEF s_aOptions[] =
2835 {
2836 /* common options */
2837 { "--provider", 'v', RTGETOPT_REQ_STRING },
2838 { "--profile", 'f', RTGETOPT_REQ_STRING },
2839
2840 { "image", kCloud_Image, RTGETOPT_REQ_NOTHING },
2841 { "instance", kCloud_Instance, RTGETOPT_REQ_NOTHING },
2842 { "list", kCloud_List, RTGETOPT_REQ_NOTHING },
2843 { "machine", kCloud_Machine, RTGETOPT_REQ_NOTHING },
2844 { "network", kCloud_Network, RTGETOPT_REQ_NOTHING },
2845 { "object", kCloud_Object, RTGETOPT_REQ_NOTHING },
2846 { "showvminfo", kCloud_ShowVMInfo, RTGETOPT_REQ_NOTHING },
2847 { "volume", kCloud_Volume, RTGETOPT_REQ_NOTHING },
2848 };
2849
2850 if (a->argc < 1)
2851 return errorNoSubcommand();
2852
2853 RTGETOPTSTATE GetState;
2854 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0);
2855 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
2856
2857 CLOUDCOMMONOPT commonOpts = { {NULL, NULL}, {NULL, NULL} };
2858 int c;
2859 RTGETOPTUNION ValueUnion;
2860 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
2861 {
2862 switch (c)
2863 {
2864 case 'v': // --provider
2865 commonOpts.provider.pszProviderName = ValueUnion.psz;
2866 break;
2867
2868 case 'f': // --profile
2869 commonOpts.profile.pszProfileName = ValueUnion.psz;
2870 break;
2871
2872 /* Sub-commands: */
2873 case kCloud_List:
2874 return handleCloudLists(a, GetState.iNext, &commonOpts);
2875
2876 case kCloud_Image:
2877 return handleCloudImage(a, GetState.iNext, &commonOpts);
2878
2879 case kCloud_Instance:
2880 return handleCloudInstance(a, GetState.iNext, &commonOpts);
2881
2882#ifdef VBOX_WITH_CLOUD_NET
2883 case kCloud_Network:
2884 return handleCloudNetwork(a, GetState.iNext, &commonOpts);
2885#endif /* VBOX_WITH_CLOUD_NET */
2886
2887 /* "cloud machine ..." handling is in VBoxManageCloudMachine.cpp */
2888 case kCloud_Machine:
2889 return handleCloudMachine(a, GetState.iNext,
2890 commonOpts.provider.pszProviderName,
2891 commonOpts.profile.pszProfileName);
2892
2893 /* ... including aliases that mimic the local vm commands */
2894 case kCloud_ShowVMInfo:
2895 return handleCloudShowVMInfo(a, GetState.iNext,
2896 commonOpts.provider.pszProviderName,
2897 commonOpts.profile.pszProfileName);
2898
2899 case VINF_GETOPT_NOT_OPTION:
2900 return errorUnknownSubcommand(ValueUnion.psz);
2901
2902 default:
2903 return errorGetOpt(c, &ValueUnion);
2904 }
2905 }
2906
2907 return errorNoSubcommand();
2908}
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