VirtualBox

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

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

VBoxManage/cloud: create instance --shape-cpus and --shape-memory
options for flexible shapes. bugref:10158.

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