VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/CloudGateway.cpp@ 93231

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

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 34.5 KB
Line 
1/* $Id: CloudGateway.cpp 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 * Implementation of local and cloud gateway management.
4 */
5
6/*
7 * Copyright (C) 2019-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#define LOG_GROUP LOG_GROUP_MAIN_CONSOLE
19
20/* Make sure all the stdint.h macros are included - must come first! */
21#ifndef __STDC_LIMIT_MACROS
22# define __STDC_LIMIT_MACROS
23#endif
24#ifndef __STDC_CONSTANT_MACROS
25# define __STDC_CONSTANT_MACROS
26#endif
27
28#include "LoggingNew.h"
29#include "ApplianceImpl.h"
30#include "CloudNetworkImpl.h"
31#include "CloudGateway.h"
32
33#include <iprt/http.h>
34#include <iprt/inifile.h>
35#include <iprt/net.h>
36#include <iprt/path.h>
37#include <iprt/vfs.h>
38#include <iprt/uri.h>
39
40static HRESULT setMacAddress(const Utf8Str& str, RTMAC& mac)
41{
42 int rc = RTNetStrToMacAddr(str.c_str(), &mac);
43 if (RT_FAILURE(rc))
44 {
45 LogRel(("CLOUD-NET: Invalid MAC address '%s'\n", str.c_str()));
46 return E_INVALIDARG;
47 }
48 return S_OK;
49}
50
51HRESULT GatewayInfo::setCloudMacAddress(const Utf8Str& mac)
52{
53 return setMacAddress(mac, mCloudMacAddress);
54}
55
56HRESULT GatewayInfo::setLocalMacAddress(const Utf8Str& mac)
57{
58 return setMacAddress(mac, mLocalMacAddress);
59}
60
61Utf8Str GatewayInfo::getCloudMacAddressWithoutColons() const
62{
63 return Utf8StrFmt("%02X%02X%02X%02X%02X%02X",
64 mCloudMacAddress.au8[0], mCloudMacAddress.au8[1], mCloudMacAddress.au8[2],
65 mCloudMacAddress.au8[3], mCloudMacAddress.au8[4], mCloudMacAddress.au8[5]);
66}
67
68Utf8Str GatewayInfo::getLocalMacAddressWithoutColons() const
69{
70 return Utf8StrFmt("%02X%02X%02X%02X%02X%02X",
71 mLocalMacAddress.au8[0], mLocalMacAddress.au8[1], mLocalMacAddress.au8[2],
72 mLocalMacAddress.au8[3], mLocalMacAddress.au8[4], mLocalMacAddress.au8[5]);
73}
74
75Utf8Str GatewayInfo::getLocalMacAddressWithColons() const
76{
77 return Utf8StrFmt("%RTmac", &mLocalMacAddress);
78}
79
80class CloudError
81{
82public:
83 CloudError(HRESULT hrc, const Utf8Str& strText) : mHrc(hrc), mText(strText) {};
84 HRESULT getRc() { return mHrc; };
85 Utf8Str getText() { return mText; };
86
87private:
88 HRESULT mHrc;
89 Utf8Str mText;
90};
91
92static void handleErrors(HRESULT hrc, const char *pszFormat, ...)
93{
94 if (FAILED(hrc))
95 {
96 va_list va;
97 va_start(va, pszFormat);
98 Utf8Str strError(pszFormat, va);
99 va_end(va);
100 LogRel(("CLOUD-NET: %s (rc=%x)\n", strError.c_str(), hrc));
101 throw CloudError(hrc, strError);
102 }
103
104}
105
106class CloudClient
107{
108public:
109 CloudClient(ComPtr<IVirtualBox> virtualBox, const Bstr& strProvider, const Bstr& strProfile);
110 ~CloudClient() {};
111
112 void startCloudGateway(const ComPtr<ICloudNetwork> &network, GatewayInfo& gateways);
113 void stopCloudGateway(const GatewayInfo& gateways);
114
115private:
116 ComPtr<ICloudProviderManager> mManager;
117 ComPtr<ICloudProvider> mProvider;
118 ComPtr<ICloudProfile> mProfile;
119 ComPtr<ICloudClient> mClient;
120};
121
122CloudClient::CloudClient(ComPtr<IVirtualBox> virtualBox, const Bstr& strProvider, const Bstr& strProfile)
123{
124 HRESULT hrc = virtualBox->COMGETTER(CloudProviderManager)(mManager.asOutParam());
125 handleErrors(hrc, "Failed to obtain cloud provider manager object");
126 hrc = mManager->GetProviderByShortName(strProvider.raw(), mProvider.asOutParam());
127 handleErrors(hrc, "Failed to obtain cloud provider '%ls'", strProvider.raw());
128 hrc = mProvider->GetProfileByName(strProfile.raw(), mProfile.asOutParam());
129 handleErrors(hrc, "Failed to obtain cloud profile '%ls'", strProfile.raw());
130 hrc = mProfile->CreateCloudClient(mClient.asOutParam());
131 handleErrors(hrc, "Failed to create cloud client");
132}
133
134void CloudClient::startCloudGateway(const ComPtr<ICloudNetwork> &network, GatewayInfo& gateways)
135{
136 ComPtr<IProgress> progress;
137 ComPtr<ICloudNetworkGatewayInfo> gatewayInfo;
138 HRESULT hrc = mClient->StartCloudNetworkGateway(network, Bstr(gateways.mPublicSshKey).raw(),
139 gatewayInfo.asOutParam(), progress.asOutParam());
140 handleErrors(hrc, "Failed to launch compute instance");
141 hrc = progress->WaitForCompletion(-1);
142 handleErrors(hrc, "Failed to launch compute instance (wait)");
143
144 Bstr instanceId;
145 hrc = gatewayInfo->COMGETTER(InstanceId)(instanceId.asOutParam());
146 handleErrors(hrc, "Failed to get launched compute instance id");
147 gateways.mGatewayInstanceId = instanceId;
148
149 Bstr publicIP;
150 hrc = gatewayInfo->COMGETTER(PublicIP)(publicIP.asOutParam());
151 handleErrors(hrc, "Failed to get cloud gateway public IP address");
152 gateways.mCloudPublicIp = publicIP;
153
154 Bstr secondaryPublicIP;
155 hrc = gatewayInfo->COMGETTER(SecondaryPublicIP)(secondaryPublicIP.asOutParam());
156 handleErrors(hrc, "Failed to get cloud gateway secondary public IP address");
157 gateways.mCloudSecondaryPublicIp = secondaryPublicIP;
158
159 Bstr macAddress;
160 hrc = gatewayInfo->COMGETTER(MacAddress)(macAddress.asOutParam());
161 handleErrors(hrc, "Failed to get cloud gateway public IP address");
162 gateways.setCloudMacAddress(macAddress);
163}
164
165void CloudClient::stopCloudGateway(const GatewayInfo& gateways)
166{
167 ComPtr<IProgress> progress;
168 HRESULT hrc = mClient->TerminateInstance(Bstr(gateways.mGatewayInstanceId).raw(), progress.asOutParam());
169 handleErrors(hrc, "Failed to terminate compute instance");
170#if 0
171 /* Someday we may want to wait until the cloud gateway has terminated. */
172 hrc = progress->WaitForCompletion(-1);
173 handleErrors(hrc, "Failed to terminate compute instance (wait)");
174#endif
175}
176
177
178static HRESULT startCloudGateway(ComPtr<IVirtualBox> virtualBox, ComPtr<ICloudNetwork> network, GatewayInfo& gateways)
179{
180 HRESULT hrc = S_OK;
181
182 try {
183 hrc = network->COMGETTER(Provider)(gateways.mCloudProvider.asOutParam());
184 hrc = network->COMGETTER(Profile)(gateways.mCloudProfile.asOutParam());
185 CloudClient client(virtualBox, gateways.mCloudProvider, gateways.mCloudProfile);
186 client.startCloudGateway(network, gateways);
187 }
188 catch (CloudError e)
189 {
190 hrc = e.getRc();
191 }
192
193 return hrc;
194}
195
196
197static HRESULT attachToLocalNetwork(ComPtr<ISession> aSession, const com::Utf8Str &aCloudNetwork)
198{
199 ComPtr<IMachine> sessionMachine;
200 HRESULT hrc = aSession->COMGETTER(Machine)(sessionMachine.asOutParam());
201 if (FAILED(hrc))
202 {
203 LogRel(("CLOUD-NET: Failed to obtain a mutable machine. hrc=%x\n", hrc));
204 return hrc;
205 }
206
207 ComPtr<INetworkAdapter> networkAdapter;
208 hrc = sessionMachine->GetNetworkAdapter(1, networkAdapter.asOutParam());
209 if (FAILED(hrc))
210 {
211 LogRel(("CLOUD-NET: Failed to locate the second network adapter. hrc=%x\n", hrc));
212 return hrc;
213 }
214
215 BstrFmt network("cloud-%s", aCloudNetwork.c_str());
216 hrc = networkAdapter->COMSETTER(InternalNetwork)(network.raw());
217 if (FAILED(hrc))
218 {
219 LogRel(("CLOUD-NET: Failed to set network name for the second network adapter. hrc=%x\n", hrc));
220 return hrc;
221 }
222
223 hrc = sessionMachine->SaveSettings();
224 if (FAILED(hrc))
225 LogRel(("CLOUD-NET: Failed to save 'lgw' settings. hrc=%x\n", hrc));
226 return hrc;
227}
228
229static HRESULT startLocalGateway(ComPtr<IVirtualBox> virtualBox, ComPtr<ISession> aSession, const com::Utf8Str &aCloudNetwork, GatewayInfo& gateways)
230{
231 /*
232 * It would be really beneficial if we do not create a local gateway VM each time a target starts.
233 * We probably just need to make sure its configuration matches the one required by the cloud network
234 * attachment and update configuration if necessary.
235 */
236 Bstr strGatewayVM = BstrFmt("lgw-%ls", gateways.mTargetVM.raw());
237 ComPtr<IMachine> machine;
238 HRESULT hrc = virtualBox->FindMachine(strGatewayVM.raw(), machine.asOutParam());
239 if (SUCCEEDED(hrc))
240 {
241 hrc = machine->LockMachine(aSession, LockType_Write);
242 if (FAILED(hrc))
243 {
244 LogRel(("CLOUD-NET: Failed to lock '%ls' for modifications. hrc=%x\n", strGatewayVM.raw(), hrc));
245 return hrc;
246 }
247
248 hrc = attachToLocalNetwork(aSession, aCloudNetwork);
249 }
250 else
251 {
252 SafeArray<IN_BSTR> groups;
253 groups.push_back(Bstr("/gateways").mutableRaw());
254 hrc = virtualBox->CreateMachine(NULL, Bstr(strGatewayVM).raw(), ComSafeArrayAsInParam(groups), Bstr("Ubuntu_64").raw(), Bstr("").raw(), machine.asOutParam());
255 if (FAILED(hrc))
256 {
257 LogRel(("CLOUD-NET: Failed to create '%ls'. hrc=%x\n", strGatewayVM.raw(), hrc));
258 return hrc;
259 }
260 /* Initial configuration */
261 hrc = machine->ApplyDefaults(NULL);
262 if (FAILED(hrc))
263 {
264 LogRel(("CLOUD-NET: Failed to apply defaults to '%ls'. hrc=%x\n", strGatewayVM.raw(), hrc));
265 return hrc;
266 }
267
268 /* Add second network adapter */
269 ComPtr<INetworkAdapter> networkAdapter;
270 hrc = machine->GetNetworkAdapter(1, networkAdapter.asOutParam());
271 if (FAILED(hrc))
272 {
273 LogRel(("CLOUD-NET: Failed to locate the second network adapter. hrc=%x\n", hrc));
274 return hrc;
275 }
276
277 hrc = networkAdapter->COMSETTER(AttachmentType)(NetworkAttachmentType_Internal);
278 if (FAILED(hrc))
279 {
280 LogRel(("CLOUD-NET: Failed to set attachment type for the second network adapter. hrc=%x\n", hrc));
281 return hrc;
282 }
283
284 BstrFmt network("cloud-%s", aCloudNetwork.c_str());
285 hrc = networkAdapter->COMSETTER(InternalNetwork)(network.raw());
286 if (FAILED(hrc))
287 {
288 LogRel(("CLOUD-NET: Failed to set network name for the second network adapter. hrc=%x\n", hrc));
289 return hrc;
290 }
291
292 hrc = networkAdapter->COMSETTER(PromiscModePolicy)(NetworkAdapterPromiscModePolicy_AllowAll);
293 if (FAILED(hrc))
294 {
295 LogRel(("CLOUD-NET: Failed to set promiscuous mode policy for the second network adapter. hrc=%x\n", hrc));
296 return hrc;
297 }
298
299 hrc = networkAdapter->COMSETTER(Enabled)(TRUE);
300 if (FAILED(hrc))
301 {
302 LogRel(("CLOUD-NET: Failed to enable the second network adapter. hrc=%x\n", hrc));
303 return hrc;
304 }
305
306 /* No need for audio -- disable it. */
307 ComPtr<IAudioAdapter> audioAdapter;
308 hrc = machine->GetNetworkAdapter(1, networkAdapter.asOutParam());
309 if (FAILED(hrc))
310 {
311 LogRel(("CLOUD-NET: Failed to locate the second network adapter. hrc=%x\n", hrc));
312 return hrc;
313 }
314
315 hrc = machine->COMGETTER(AudioAdapter)(audioAdapter.asOutParam());
316 if (FAILED(hrc))
317 {
318 LogRel(("CLOUD-NET: Failed to set attachment type for the second network adapter. hrc=%x\n", hrc));
319 return hrc;
320 }
321
322 hrc = audioAdapter->COMSETTER(Enabled)(FALSE);
323 if (FAILED(hrc))
324 {
325 LogRel(("CLOUD-NET: Failed to disable the audio adapter. hrc=%x\n", hrc));
326 return hrc;
327 }
328
329 /** @todo Disable USB? */
330
331 /* Register the local gateway VM */
332 hrc = virtualBox->RegisterMachine(machine);
333 if (FAILED(hrc))
334 {
335 LogRel(("CLOUD-NET: Failed to register '%ls'. hrc=%x\n", strGatewayVM.raw(), hrc));
336 return hrc;
337 }
338
339 /*
340 * Storage can only be attached to registered VMs which means we need to use session
341 * to lock VM in order to make it mutable again.
342 */
343 ComPtr<ISystemProperties> systemProperties;
344 ComPtr<IMedium> hd;
345 Bstr defaultMachineFolder;
346 hrc = virtualBox->COMGETTER(SystemProperties)(systemProperties.asOutParam());
347 if (FAILED(hrc))
348 {
349 LogRel(("CLOUD-NET: Failed to obtain system properties. hrc=%x\n", hrc));
350 return hrc;
351 }
352 hrc = systemProperties->COMGETTER(DefaultMachineFolder)(defaultMachineFolder.asOutParam());
353 if (FAILED(hrc))
354 {
355 LogRel(("CLOUD-NET: Failed to obtain default machine folder. hrc=%x\n", hrc));
356 return hrc;
357 }
358 hrc = virtualBox->OpenMedium(BstrFmt("%ls\\gateways\\lgw.vdi", defaultMachineFolder.raw()).raw(), DeviceType_HardDisk, AccessMode_ReadWrite, FALSE, hd.asOutParam());
359 if (FAILED(hrc))
360 {
361 LogRel(("CLOUD-NET: Failed to open medium for '%ls'. hrc=%x\n", strGatewayVM.raw(), hrc));
362 return hrc;
363 }
364
365 hrc = machine->LockMachine(aSession, LockType_Write);
366 if (FAILED(hrc))
367 {
368 LogRel(("CLOUD-NET: Failed to lock '%ls' for modifications. hrc=%x\n", strGatewayVM.raw(), hrc));
369 return hrc;
370 }
371
372 ComPtr<IMachine> sessionMachine;
373 hrc = aSession->COMGETTER(Machine)(sessionMachine.asOutParam());
374 if (FAILED(hrc))
375 {
376 LogRel(("CLOUD-NET: Failed to obtain a mutable machine. hrc=%x\n", hrc));
377 return hrc;
378 }
379
380 hrc = sessionMachine->AttachDevice(Bstr("SATA").raw(), 0, 0, DeviceType_HardDisk, hd);
381 if (FAILED(hrc))
382 {
383 LogRel(("CLOUD-NET: Failed to attach HD to '%ls'. hrc=%x\n", strGatewayVM.raw(), hrc));
384 return hrc;
385 }
386
387 /* Save settings */
388 hrc = sessionMachine->SaveSettings();
389 if (FAILED(hrc))
390 {
391 LogRel(("CLOUD-NET: Failed to save '%ls' settings. hrc=%x\n", strGatewayVM.raw(), hrc));
392 return hrc;
393 }
394 }
395 /* Unlock the machine before start, it will be re-locked by LaunchVMProcess */
396 aSession->UnlockMachine();
397
398#ifdef DEBUG
399 #define LGW_FRONTEND "gui"
400#else
401 #define LGW_FRONTEND "headless"
402#endif
403 ComPtr<IProgress> progress;
404 hrc = machine->LaunchVMProcess(aSession, Bstr(LGW_FRONTEND).raw(), ComSafeArrayNullInParam(), progress.asOutParam());
405 if (FAILED(hrc))
406 {
407 LogRel(("CLOUD-NET: Failed to launch '%ls'. hrc=%x\n", strGatewayVM.raw(), hrc));
408 return hrc;
409 }
410
411 hrc = progress->WaitForCompletion(-1);
412 if (FAILED(hrc))
413 LogRel(("CLOUD-NET: Failed to launch '%ls'. hrc=%x\n", strGatewayVM.raw(), hrc));
414
415 gateways.mGatewayVM = strGatewayVM;
416
417 ComPtr<IEventSource> es;
418 hrc = virtualBox->COMGETTER(EventSource)(es.asOutParam());
419 ComPtr<IEventListener> listener;
420 hrc = es->CreateListener(listener.asOutParam());
421 com::SafeArray <VBoxEventType_T> eventTypes(1);
422 eventTypes.push_back(VBoxEventType_OnGuestPropertyChanged);
423 hrc = es->RegisterListener(listener, ComSafeArrayAsInParam(eventTypes), false);
424
425 Bstr publicKey;
426 Bstr aMachStrGuid;
427 machine->COMGETTER(Id)(aMachStrGuid.asOutParam());
428 Guid aMachGuid(aMachStrGuid);
429
430 uint64_t u64Started = RTTimeMilliTS();
431 do
432 {
433 ComPtr<IEvent> ev;
434 hrc = es->GetEvent(listener, 1000 /* seconds */, ev.asOutParam());
435 if (ev)
436 {
437 VBoxEventType_T aType;
438 hrc = ev->COMGETTER(Type)(&aType);
439 if (aType == VBoxEventType_OnGuestPropertyChanged)
440 {
441 ComPtr<IGuestPropertyChangedEvent> gpcev = ev;
442 Assert(gpcev);
443 Bstr aNextStrGuid;
444 gpcev->COMGETTER(MachineId)(aNextStrGuid.asOutParam());
445 if (aMachGuid != Guid(aNextStrGuid))
446 continue;
447 Bstr aNextName;
448 gpcev->COMGETTER(Name)(aNextName.asOutParam());
449 if (aNextName == "/VirtualBox/Gateway/PublicKey")
450 {
451 gpcev->COMGETTER(Value)(publicKey.asOutParam());
452 LogRel(("CLOUD-NET: Got public key from local gateway '%ls'\n", publicKey.raw()));
453 break;
454 }
455 }
456
457 }
458 } while (RTTimeMilliTS() - u64Started < 300 * 1000); /** @todo reasonable timeout */
459
460 if (publicKey.isEmpty())
461 {
462 LogRel(("CLOUD-NET: Failed to get ssh public key from '%ls'\n", strGatewayVM.raw()));
463 return E_FAIL;
464 }
465
466 gateways.mPublicSshKey = publicKey;
467
468 return hrc;
469}
470
471static bool getProxyForIpAddr(ComPtr<IVirtualBox> virtualBox, const com::Utf8Str &strIpAddr, Bstr &strProxyType, Bstr &strProxyHost, Bstr &strProxyPort)
472{
473#ifndef VBOX_WITH_PROXY_INFO
474 RT_NOREF(virtualBox, strIpAddr, strProxyType, strProxyHost, strProxyPort);
475 LogRel(("CLOUD-NET: Proxy support is disabled. Using direct connection.\n"));
476 return false;
477#else /* VBOX_WITH_PROXY_INFO */
478 ComPtr<ISystemProperties> systemProperties;
479 ProxyMode_T enmProxyMode;
480 HRESULT hrc = virtualBox->COMGETTER(SystemProperties)(systemProperties.asOutParam());
481 if (FAILED(hrc))
482 {
483 LogRel(("CLOUD-NET: Failed to obtain system properties. hrc=%x\n", hrc));
484 return false;
485 }
486 hrc = systemProperties->COMGETTER(ProxyMode)(&enmProxyMode);
487 if (FAILED(hrc))
488 {
489 LogRel(("CLOUD-NET: Failed to obtain default machine folder. hrc=%x\n", hrc));
490 return false;
491 }
492 if (enmProxyMode == ProxyMode_NoProxy)
493 return false;
494
495 Bstr proxyUrl;
496 if (enmProxyMode == ProxyMode_Manual)
497 {
498 hrc = systemProperties->COMGETTER(ProxyURL)(proxyUrl.asOutParam());
499 if (FAILED(hrc))
500 {
501 LogRel(("CLOUD-NET: Failed to obtain proxy URL. hrc=%x\n", hrc));
502 return false;
503 }
504 Utf8Str strProxyUrl = proxyUrl;
505 if (!strProxyUrl.contains("://"))
506 strProxyUrl = "http://" + strProxyUrl;
507 const char *pcszProxyUrl = strProxyUrl.c_str();
508 RTURIPARSED Parsed;
509 int rc = RTUriParse(pcszProxyUrl, &Parsed);
510 if (RT_FAILURE(rc))
511 {
512 LogRel(("CLOUD-NET: Failed to parse proxy URL: %ls (rc=%d)\n", proxyUrl.raw(), rc));
513 return false;
514 }
515 char *pszHost = RTUriParsedAuthorityHost(pcszProxyUrl, &Parsed);
516 if (!pszHost)
517 {
518 LogRel(("CLOUD-NET: Failed to get proxy host name from proxy URL: %s\n", pcszProxyUrl));
519 return false;
520 }
521 strProxyHost = pszHost;
522 RTStrFree(pszHost);
523 char *pszScheme = RTUriParsedScheme(pcszProxyUrl, &Parsed);
524 if (!pszScheme)
525 {
526 LogRel(("CLOUD-NET: Failed to get proxy scheme from proxy URL: %s\n", pcszProxyUrl));
527 return false;
528 }
529 strProxyType = Utf8Str(pszScheme).toUpper();
530 RTStrFree(pszScheme);
531 uint32_t uProxyPort = RTUriParsedAuthorityPort(pcszProxyUrl, &Parsed);
532 if (uProxyPort == UINT32_MAX)
533 if (!pszScheme)
534 {
535 LogRel(("CLOUD-NET: Failed to get proxy port from proxy URL: %s\n", pcszProxyUrl));
536 return false;
537 }
538 strProxyPort = BstrFmt("%d", uProxyPort);
539 }
540 else
541 {
542 /* Attempt to use system proxy settings (ProxyMode_System) */
543 RTHTTP hHttp;
544 int rc = RTHttpCreate(&hHttp);
545 if (RT_FAILURE(rc))
546 {
547 LogRel(("CLOUD-NET: Failed to create HTTP context (rc=%Rrc)\n", rc));
548 return false;
549 }
550 rc = RTHttpUseSystemProxySettings(hHttp);
551 if (RT_FAILURE(rc))
552 {
553 LogRel(("CLOUD-NET: Failed to use system proxy (rc=%Rrc)\n", rc));
554 RTHttpDestroy(hHttp);
555 return false;
556 }
557
558 RTHTTPPROXYINFO proxy;
559 rc = RTHttpQueryProxyInfoForUrl(hHttp, ("http://" + strIpAddr).c_str(), &proxy);
560 if (RT_FAILURE(rc))
561 {
562 LogRel(("CLOUD-NET: Failed to get proxy for %s (rc=%Rrc)\n", strIpAddr.c_str(), rc));
563 RTHttpDestroy(hHttp);
564 return false;
565 }
566 switch (proxy.enmProxyType)
567 {
568 case RTHTTPPROXYTYPE_NOPROXY:
569 RTHttpFreeProxyInfo(&proxy);
570 RTHttpDestroy(hHttp);
571 return false;
572 case RTHTTPPROXYTYPE_HTTP:
573 strProxyType = "HTTP";
574 break;
575 case RTHTTPPROXYTYPE_HTTPS:
576 strProxyType = "HTTPS";
577 break;
578 case RTHTTPPROXYTYPE_SOCKS4:
579 strProxyType = "SOCKS4";
580 break;
581 case RTHTTPPROXYTYPE_SOCKS5:
582 strProxyType = "SOCKS5";
583 break;
584 case RTHTTPPROXYTYPE_UNKNOWN:
585 case RTHTTPPROXYTYPE_INVALID:
586 case RTHTTPPROXYTYPE_END:
587 case RTHTTPPROXYTYPE_32BIT_HACK:
588 break;
589 }
590 AssertStmt(strProxyType.isNotEmpty(), LogRel(("CLOUD-NET: Unknown proxy type: %d\n", proxy.enmProxyType)));
591 strProxyHost = proxy.pszProxyHost;
592 if (proxy.uProxyPort != UINT32_MAX)
593 strProxyPort.printf("%d", proxy.uProxyPort);
594 RTHttpFreeProxyInfo(&proxy);
595 RTHttpDestroy(hHttp);
596 }
597 return true;
598#endif /* VBOX_WITH_PROXY_INFO */
599}
600
601
602static HRESULT exchangeInfoBetweenGateways(ComPtr<IVirtualBox> virtualBox, ComPtr<ISession> aSession, GatewayInfo& gateways)
603{
604 RT_NOREF(virtualBox);
605 HRESULT hrc = S_OK;
606 do
607 {
608 ComPtr<IConsole> console;
609 hrc = aSession->COMGETTER(Console)(console.asOutParam());
610 if (FAILED(hrc))
611 {
612 LogRel(("CLOUD-NET: Failed to obtain console for 'lgw'. hrc=%x\n", hrc));
613 break;
614 }
615
616 ComPtr<IGuest> guest;
617 hrc = console->COMGETTER(Guest)(guest.asOutParam());
618 if (FAILED(hrc))
619 {
620 LogRel(("CLOUD-NET: Failed to obtain guest for 'lgw'. hrc=%x\n", hrc));
621 break;
622 }
623
624 ComPtr<IGuestSession> guestSession;
625
626 GuestSessionWaitResult_T enmWaitResult = GuestSessionWaitResult_None;
627 for (int cTriesLeft = 6; cTriesLeft > 0; cTriesLeft--)
628 {
629 RTThreadSleep(5000 /* ms */);
630 hrc = guest->CreateSession(Bstr("vbox").raw(), Bstr("vbox").raw(), NULL, Bstr("Cloud Gateway Impersonation").raw(), guestSession.asOutParam());
631 if (FAILED(hrc))
632 {
633 LogRel(("CLOUD-NET: Failed to create guest session for 'lgw'%s. hrc=%x\n", cTriesLeft > 1 ? ", will re-try" : "", hrc));
634 continue;
635 }
636 hrc = guestSession->WaitFor(GuestSessionWaitForFlag_Start, 30 * 1000, &enmWaitResult);
637 if (FAILED(hrc))
638 {
639 LogRel(("CLOUD-NET: WARNING! Failed to wait in guest session for 'lgw'%s. waitResult=%x hrc=%x\n",
640 cTriesLeft > 1 ? ", will re-try" : "", enmWaitResult, hrc));
641 guestSession->Close();
642 guestSession.setNull();
643 continue;
644 }
645 if (enmWaitResult == GuestSessionWaitResult_Start)
646 break;
647 LogRel(("CLOUD-NET: WARNING! 'lgw' guest session waitResult=%x%s\n",
648 enmWaitResult, cTriesLeft > 1 ? ", will re-try" : ""));
649 guestSession->Close();
650 guestSession.setNull();
651 }
652
653 if (FAILED(hrc))
654 {
655 LogRel(("CLOUD-NET: Failed to start guest session for 'lgw'. waitResult=%x hrc=%x\n", enmWaitResult, hrc));
656 break;
657 }
658
659 GuestSessionStatus_T enmSessionStatus;
660 hrc = guestSession->COMGETTER(Status)(&enmSessionStatus);
661 if (FAILED(hrc))
662 {
663 LogRel(("CLOUD-NET: Failed to get guest session status for 'lgw'. hrc=%x\n", hrc));
664 break;
665 }
666 LogRel(("CLOUD-NET: Session status: %d\n", enmSessionStatus));
667
668 Bstr strPrimaryProxyType;
669 Bstr strPrimaryProxyHost;
670 Bstr strPrimaryProxyPort;
671 Bstr strSecondaryProxyType;
672 Bstr strSecondaryProxyHost;
673 Bstr strSecondaryProxyPort;
674
675 ComPtr<IGuestProcess> guestProcess;
676 com::SafeArray<IN_BSTR> aArgs;
677 com::SafeArray<IN_BSTR> aEnv;
678 com::SafeArray<ProcessCreateFlag_T> aCreateFlags;
679 com::SafeArray<ProcessWaitForFlag_T> aWaitFlags;
680 aCreateFlags.push_back(ProcessCreateFlag_WaitForStdOut);
681 aCreateFlags.push_back(ProcessCreateFlag_WaitForStdErr);
682#define GUEST_CMD "/bin/sh"
683 aArgs.push_back(Bstr(GUEST_CMD).mutableRaw());
684 aArgs.push_back(Bstr("-x").mutableRaw());
685 aArgs.push_back(Bstr("/home/vbox/local-bridge.sh").mutableRaw());
686 aArgs.push_back(Bstr(gateways.mCloudPublicIp).mutableRaw());
687 aArgs.push_back(Bstr(gateways.mCloudSecondaryPublicIp).mutableRaw());
688 aArgs.push_back(Bstr(gateways.getLocalMacAddressWithColons()).mutableRaw());
689 if (getProxyForIpAddr(virtualBox, gateways.mCloudPublicIp, strPrimaryProxyType, strPrimaryProxyHost, strPrimaryProxyPort))
690 {
691 aArgs.push_back(strPrimaryProxyType.mutableRaw());
692 aArgs.push_back(strPrimaryProxyHost.mutableRaw());
693 aArgs.push_back(strPrimaryProxyPort.mutableRaw());
694 if (getProxyForIpAddr(virtualBox, gateways.mCloudSecondaryPublicIp, strSecondaryProxyType, strSecondaryProxyHost, strSecondaryProxyPort))
695 {
696 aArgs.push_back(strSecondaryProxyType.mutableRaw());
697 aArgs.push_back(strSecondaryProxyHost.mutableRaw());
698 aArgs.push_back(strSecondaryProxyPort.mutableRaw());
699 }
700 }
701 hrc = guestSession->ProcessCreate(Bstr(GUEST_CMD).raw(),
702 ComSafeArrayAsInParam(aArgs),
703 ComSafeArrayAsInParam(aEnv),
704 ComSafeArrayAsInParam(aCreateFlags),
705 180 * 1000 /* ms */,
706 guestProcess.asOutParam());
707 if (FAILED(hrc))
708 {
709 LogRel(("CLOUD-NET: Failed to create guest process '/bin/sh' for 'lgw'. hrc=%x\n", hrc));
710 break;
711 }
712
713 ProcessWaitResult_T waitResult;
714 hrc = guestProcess->WaitFor(ProcessWaitForFlag_Start, 10 * 1000 /* ms */, &waitResult);
715 if (FAILED(hrc) || (waitResult != ProcessWaitResult_Start))
716 {
717 LogRel(("CLOUD-NET: Failed to wait for guest process to start for 'lgw'. waitResult=%x hrc=%x\n",
718 waitResult, hrc));
719 break;
720 }
721 LogRel(("CLOUD-NET: waitResult=%x\n", waitResult));
722
723 uint32_t cNotSupported = 0;
724 bool fRead, fDone = false;
725 uint64_t u64Start = RTTimeMilliTS();
726 do
727 {
728 /** @todo wait for stdout when it becomes supported! */
729 hrc = guestProcess->WaitFor(ProcessWaitForFlag_Terminate | ProcessWaitForFlag_StdOut, 1000 /* ms */, &waitResult);
730 if (FAILED(hrc))
731 {
732 LogRel(("CLOUD-NET: Failed to get output from guest process for 'lgw'. waitResult=%x hrc=%x\n",
733 waitResult, hrc));
734 break;
735 }
736 if (waitResult == ProcessWaitResult_WaitFlagNotSupported)
737 ++cNotSupported;
738 else
739 {
740 if (cNotSupported)
741 {
742 LogRel(("CLOUD-NET: waitResult=9, repeated %u times\n", cNotSupported));
743 cNotSupported = 0;
744 }
745 LogRel(("CLOUD-NET: waitResult=%x\n", waitResult));
746 }
747
748 fRead = false;
749 switch (waitResult)
750 {
751 case ProcessWaitResult_WaitFlagNotSupported:
752 RTThreadYield();
753 /* Fall through */
754 case ProcessWaitResult_StdOut:
755 fRead = true;
756 break;
757 case ProcessWaitResult_Terminate:
758 fDone = true;
759 break;
760 case ProcessWaitResult_Timeout:
761 {
762 ProcessStatus_T enmProcStatus;
763 hrc = guestProcess->COMGETTER(Status)(&enmProcStatus);
764 if (FAILED(hrc))
765 {
766 LogRel(("CLOUD-NET: Failed to query guest process status for 'lgw'. hrc=%x\n", hrc));
767 fDone = true;
768 }
769 else
770 {
771 LogRel(("CLOUD-NET: Guest process timeout for 'lgw'. status=%d\n", enmProcStatus));
772 if ( enmProcStatus == ProcessStatus_TimedOutKilled
773 || enmProcStatus == ProcessStatus_TimedOutAbnormally)
774 fDone = true;
775 }
776 fRead = true;
777 break;
778 }
779 default:
780 LogRel(("CLOUD-NET: Unexpected waitResult=%x\n", waitResult));
781 break;
782 }
783
784 if (fRead)
785 {
786 SafeArray<BYTE> aStdOutData, aStdErrData;
787 hrc = guestProcess->Read(1 /* StdOut */, _64K, 60 * 1000 /* ms */, ComSafeArrayAsOutParam(aStdOutData));
788 if (FAILED(hrc))
789 {
790 LogRel(("CLOUD-NET: Failed to read stdout from guest process for 'lgw'. hrc=%x\n", hrc));
791 break;
792 }
793 hrc = guestProcess->Read(2 /* StdErr */, _64K, 60 * 1000 /* ms */, ComSafeArrayAsOutParam(aStdErrData));
794 if (FAILED(hrc))
795 {
796 LogRel(("CLOUD-NET: Failed to read stderr from guest process for 'lgw'. hrc=%x\n", hrc));
797 break;
798 }
799
800 size_t cbStdOutData = aStdOutData.size();
801 size_t cbStdErrData = aStdErrData.size();
802 if (cbStdOutData == 0 && cbStdErrData == 0)
803 {
804 //LogRel(("CLOUD-NET: Empty output from guest process for 'lgw'. hrc=%x\n", hrc));
805 continue;
806 }
807
808 if (cNotSupported)
809 {
810 LogRel(("CLOUD-NET: waitResult=9, repeated %u times\n", cNotSupported));
811 cNotSupported = 0;
812 }
813 if (cbStdOutData)
814 LogRel(("CLOUD-NET: Got stdout from 'lgw':\n%.*s", aStdOutData.size(), aStdOutData.raw()));
815 if (cbStdErrData)
816 LogRel(("CLOUD-NET: Got stderr from 'lgw':\n%.*s", aStdErrData.size(), aStdErrData.raw()));
817 }
818 }
819 while (!fDone && RTTimeMilliTS() - u64Start < 180 * 1000 /* ms */);
820
821 } while (false);
822
823 return hrc;
824}
825
826
827HRESULT destroyLocalGateway(ComPtr<IVirtualBox> virtualBox, const GatewayInfo& gateways)
828{
829 if (gateways.mGatewayVM.isEmpty())
830 return S_OK;
831
832 LogRel(("CLOUD-NET: Shutting down local gateway '%s'...\n", gateways.mGatewayVM.c_str()));
833
834 ComPtr<IMachine> localGateway;
835 HRESULT hrc = virtualBox->FindMachine(Bstr(gateways.mGatewayVM).raw(), localGateway.asOutParam());
836 if (FAILED(hrc))
837 {
838 LogRel(("CLOUD-NET: Failed to locate '%s'. hrc=%x\n", gateways.mGatewayVM.c_str(), hrc));
839 return hrc;
840 }
841
842 MachineState_T tmp;
843 hrc = localGateway->COMGETTER(State)(&tmp);
844 if (tmp == MachineState_Running)
845 {
846 /* If the gateway VM is running we need to stop it */
847 ComPtr<ISession> session;
848 hrc = session.createInprocObject(CLSID_Session);
849 if (FAILED(hrc))
850 {
851 LogRel(("CLOUD-NET: Failed to create a session. hrc=%x\n", hrc));
852 return hrc;
853 }
854
855 hrc = localGateway->LockMachine(session, LockType_Shared);
856 if (FAILED(hrc))
857 {
858 LogRel(("CLOUD-NET: Failed to lock '%s' for control. hrc=%x\n", gateways.mGatewayVM.c_str(), hrc));
859 return hrc;
860 }
861
862 ComPtr<IConsole> console;
863 hrc = session->COMGETTER(Console)(console.asOutParam());
864 if (FAILED(hrc))
865 {
866 LogRel(("CLOUD-NET: Failed to obtain console for '%s'. hrc=%x\n", gateways.mGatewayVM.c_str(), hrc));
867 return hrc;
868 }
869
870 ComPtr<IProgress> progress;
871 console->PowerDown(progress.asOutParam()); /* We assume the gateway disk to be immutable! */
872
873#if 0
874 hrc = progress->WaitForCompletion(-1);
875 if (FAILED(hrc))
876 LogRel(("CLOUD-NET: Failed to stop '%s'. hrc=%x\n", gateways.mGatewayVM.c_str(), hrc));
877#endif
878 session->UnlockMachine();
879 }
880#if 0
881 /*
882 * Unfortunately we cannot unregister a machine we've just powered down and unlocked.
883 * It takes some time for the machine to unlock completely.
884 */
885 /** @todo Removal of VM should probably be optional in the future. */
886 SafeIfaceArray<IMedium> media;
887 hrc = pLocalGateway->Unregister(CleanupMode_DetachAllReturnHardDisksOnly, ComSafeArrayAsOutParam(media));
888 if (FAILED(hrc))
889 {
890 LogRel(("CLOUD-NET: Failed to unregister '%s'. hrc=%x\n", gateways.mGatewayVM.c_str(), hrc));
891 return hrc;
892 }
893 ComPtr<IProgress> removalProgress;
894 hrc = pLocalGateway->DeleteConfig(ComSafeArrayAsInParam(media), removalProgress.asOutParam());
895 if (FAILED(hrc))
896 {
897 LogRel(("CLOUD-NET: Failed to delete machine files for '%s'. hrc=%x\n", gateways.mGatewayVM.c_str(), hrc));
898 }
899#endif
900 return hrc;
901}
902
903static HRESULT terminateCloudGateway(ComPtr<IVirtualBox> virtualBox, const GatewayInfo& gateways)
904{
905 if (gateways.mGatewayInstanceId.isEmpty())
906 return S_OK;
907
908 LogRel(("CLOUD-NET: Terminating cloud gateway instance '%s'...\n", gateways.mGatewayInstanceId.c_str()));
909
910 HRESULT hrc = S_OK;
911 try {
912 CloudClient client(virtualBox, gateways.mCloudProvider, gateways.mCloudProfile);
913 client.stopCloudGateway(gateways);
914 }
915 catch (CloudError e)
916 {
917 hrc = e.getRc();
918 LogRel(("CLOUD-NET: Failed to terminate cloud gateway instance (rc=%x).\n", hrc));
919 }
920 return hrc;
921}
922
923HRESULT startGateways(ComPtr<IVirtualBox> virtualBox, ComPtr<ICloudNetwork> network, GatewayInfo& gateways)
924{
925 /* Session used to launch and control the local gateway VM */
926 ComPtr<ISession> session;
927 Bstr strNetwork;
928 HRESULT hrc = session.createInprocObject(CLSID_Session);
929 if (FAILED(hrc))
930 LogRel(("CLOUD-NET: Failed to create a session. hrc=%x\n", hrc));
931 else
932 hrc = network->COMGETTER(NetworkName)(strNetwork.asOutParam());
933 if (SUCCEEDED(hrc))
934 hrc = startLocalGateway(virtualBox, session, strNetwork, gateways);
935 if (SUCCEEDED(hrc))
936 hrc = startCloudGateway(virtualBox, network, gateways);
937 if (SUCCEEDED(hrc))
938 hrc = exchangeInfoBetweenGateways(virtualBox, session, gateways);
939
940 session->UnlockMachine();
941 /** @todo Destroy gateways if unsuccessful (cleanup) */
942
943 return hrc;
944}
945
946HRESULT stopGateways(ComPtr<IVirtualBox> virtualBox, const GatewayInfo& gateways)
947{
948 HRESULT hrc = destroyLocalGateway(virtualBox, gateways);
949 AssertComRC(hrc);
950 hrc = terminateCloudGateway(virtualBox, gateways);
951 AssertComRC(hrc);
952 return hrc;
953}
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