VirtualBox

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

Last change on this file since 85359 was 85359, checked in by vboxsync, 4 years ago

OCI: (bugref:9469) Enable cloud network feature in OSE, LogRel messages and copyright fixes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 34.1 KB
Line 
1/* $Id: CloudGateway.cpp 85359 2020-07-15 19:08:02Z vboxsync $ */
2/** @file
3 * Implementation of local and cloud gateway management.
4 */
5
6/*
7 * Copyright (C) 2019-2020 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=%d)\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=%d)\n", rc));
554 RTHttpDestroy(hHttp);
555 return false;
556 }
557
558 RTHTTPPROXYINFO proxy;
559 RT_ZERO(proxy);
560 rc = RTHttpGetProxyInfoForUrl(hHttp, ("http://" + strIpAddr).c_str(), &proxy);
561 if (RT_FAILURE(rc))
562 {
563 LogRel(("CLOUD-NET: Failed to get proxy for %s (rc=%d)\n", strIpAddr.c_str(), rc));
564 RTHttpDestroy(hHttp);
565 return false;
566 }
567 switch (proxy.enmProxyType)
568 {
569 case RTHTTPPROXYTYPE_HTTP:
570 strProxyType = "HTTP";
571 break;
572 case RTHTTPPROXYTYPE_HTTPS:
573 strProxyType = "HTTPS";
574 break;
575 case RTHTTPPROXYTYPE_SOCKS4:
576 strProxyType = "SOCKS4";
577 break;
578 case RTHTTPPROXYTYPE_SOCKS5:
579 strProxyType = "SOCKS5";
580 break;
581 case RTHTTPPROXYTYPE_UNKNOWN:
582 LogRel(("CLOUD-NET: Unknown proxy type."));
583 break;
584 }
585 strProxyHost = proxy.pszProxyHost;
586 strProxyPort = BstrFmt("%d", proxy.uProxyPort);
587 RTHttpFreeProxyInfo(&proxy);
588 RTHttpDestroy(hHttp);
589 }
590 return true;
591#endif /* VBOX_WITH_PROXY_INFO */
592}
593
594
595static HRESULT exchangeInfoBetweenGateways(ComPtr<IVirtualBox> virtualBox, ComPtr<ISession> aSession, GatewayInfo& gateways)
596{
597 RT_NOREF(virtualBox);
598 HRESULT hrc = S_OK;
599 do
600 {
601 ComPtr<IConsole> console;
602 hrc = aSession->COMGETTER(Console)(console.asOutParam());
603 if (FAILED(hrc))
604 {
605 LogRel(("CLOUD-NET: Failed to obtain console for 'lgw'. hrc=%x\n", hrc));
606 break;
607 }
608
609 ComPtr<IGuest> guest;
610 hrc = console->COMGETTER(Guest)(guest.asOutParam());
611 if (FAILED(hrc))
612 {
613 LogRel(("CLOUD-NET: Failed to obtain guest for 'lgw'. hrc=%x\n", hrc));
614 break;
615 }
616
617 ComPtr<IGuestSession> guestSession;
618
619 GuestSessionWaitResult_T enmWaitResult = GuestSessionWaitResult_None;
620 for (int cTriesLeft = 6; cTriesLeft > 0; cTriesLeft--)
621 {
622 RTThreadSleep(5000 /* ms */);
623 hrc = guest->CreateSession(Bstr("vbox").raw(), Bstr("vbox").raw(), NULL, Bstr("Cloud Gateway Impersonation").raw(), guestSession.asOutParam());
624 if (FAILED(hrc))
625 {
626 LogRel(("CLOUD-NET: Failed to create guest session for 'lgw'%s. hrc=%x\n", cTriesLeft > 1 ? ", will re-try" : "", hrc));
627 continue;
628 }
629 hrc = guestSession->WaitFor(GuestSessionWaitForFlag_Start, 30 * 1000, &enmWaitResult);
630 if (FAILED(hrc))
631 {
632 LogRel(("CLOUD-NET: WARNING! Failed to wait in guest session for 'lgw'%s. waitResult=%x hrc=%x\n",
633 cTriesLeft > 1 ? ", will re-try" : "", enmWaitResult, hrc));
634 guestSession->Close();
635 guestSession.setNull();
636 continue;
637 }
638 if (enmWaitResult == GuestSessionWaitResult_Start)
639 break;
640 LogRel(("CLOUD-NET: WARNING! 'lgw' guest session waitResult=%x%s\n",
641 enmWaitResult, cTriesLeft > 1 ? ", will re-try" : ""));
642 guestSession->Close();
643 guestSession.setNull();
644 }
645
646 if (FAILED(hrc))
647 {
648 LogRel(("CLOUD-NET: Failed to start guest session for 'lgw'. waitResult=%x hrc=%x\n", enmWaitResult, hrc));
649 break;
650 }
651
652 GuestSessionStatus_T enmSessionStatus;
653 hrc = guestSession->COMGETTER(Status)(&enmSessionStatus);
654 if (FAILED(hrc))
655 {
656 LogRel(("CLOUD-NET: Failed to get guest session status for 'lgw'. hrc=%x\n", hrc));
657 break;
658 }
659 LogRel(("CLOUD-NET: Session status: %d\n", enmSessionStatus));
660
661 Bstr strPrimaryProxyType;
662 Bstr strPrimaryProxyHost;
663 Bstr strPrimaryProxyPort;
664 Bstr strSecondaryProxyType;
665 Bstr strSecondaryProxyHost;
666 Bstr strSecondaryProxyPort;
667
668 ComPtr<IGuestProcess> guestProcess;
669 com::SafeArray<IN_BSTR> aArgs;
670 com::SafeArray<IN_BSTR> aEnv;
671 com::SafeArray<ProcessCreateFlag_T> aCreateFlags;
672 com::SafeArray<ProcessWaitForFlag_T> aWaitFlags;
673 aCreateFlags.push_back(ProcessCreateFlag_WaitForStdOut);
674 aCreateFlags.push_back(ProcessCreateFlag_WaitForStdErr);
675#define GUEST_CMD "/bin/sh"
676 aArgs.push_back(Bstr(GUEST_CMD).mutableRaw());
677 aArgs.push_back(Bstr("-x").mutableRaw());
678 aArgs.push_back(Bstr("/home/vbox/local-bridge.sh").mutableRaw());
679 aArgs.push_back(Bstr(gateways.mCloudPublicIp).mutableRaw());
680 aArgs.push_back(Bstr(gateways.mCloudSecondaryPublicIp).mutableRaw());
681 aArgs.push_back(Bstr(gateways.getLocalMacAddressWithColons()).mutableRaw());
682 if (getProxyForIpAddr(virtualBox, gateways.mCloudPublicIp, strPrimaryProxyType, strPrimaryProxyHost, strPrimaryProxyPort))
683 {
684 aArgs.push_back(strPrimaryProxyType.mutableRaw());
685 aArgs.push_back(strPrimaryProxyHost.mutableRaw());
686 aArgs.push_back(strPrimaryProxyPort.mutableRaw());
687 if (getProxyForIpAddr(virtualBox, gateways.mCloudSecondaryPublicIp, strSecondaryProxyType, strSecondaryProxyHost, strSecondaryProxyPort))
688 {
689 aArgs.push_back(strSecondaryProxyType.mutableRaw());
690 aArgs.push_back(strSecondaryProxyHost.mutableRaw());
691 aArgs.push_back(strSecondaryProxyPort.mutableRaw());
692 }
693 }
694 hrc = guestSession->ProcessCreate(Bstr(GUEST_CMD).raw(),
695 ComSafeArrayAsInParam(aArgs),
696 ComSafeArrayAsInParam(aEnv),
697 ComSafeArrayAsInParam(aCreateFlags),
698 180 * 1000 /* ms */,
699 guestProcess.asOutParam());
700 if (FAILED(hrc))
701 {
702 LogRel(("CLOUD-NET: Failed to create guest process '/bin/sh' for 'lgw'. hrc=%x\n", hrc));
703 break;
704 }
705
706 ProcessWaitResult_T waitResult;
707 hrc = guestProcess->WaitFor(ProcessWaitForFlag_Start, 10 * 1000 /* ms */, &waitResult);
708 if (FAILED(hrc) || (waitResult != ProcessWaitResult_Start))
709 {
710 LogRel(("CLOUD-NET: Failed to wait for guest process to start for 'lgw'. waitResult=%x hrc=%x\n",
711 waitResult, hrc));
712 break;
713 }
714 LogRel(("CLOUD-NET: waitResult=%x\n", waitResult));
715
716 uint32_t cNotSupported = 0;
717 bool fRead, fDone = false;
718 uint64_t u64Start = RTTimeMilliTS();
719 do
720 {
721 /** @todo wait for stdout when it becomes supported! */
722 hrc = guestProcess->WaitFor(ProcessWaitForFlag_Terminate | ProcessWaitForFlag_StdOut, 1000 /* ms */, &waitResult);
723 if (FAILED(hrc))
724 {
725 LogRel(("CLOUD-NET: Failed to get output from guest process for 'lgw'. waitResult=%x hrc=%x\n",
726 waitResult, hrc));
727 break;
728 }
729 if (waitResult == ProcessWaitResult_WaitFlagNotSupported)
730 ++cNotSupported;
731 else
732 {
733 if (cNotSupported)
734 {
735 LogRel(("CLOUD-NET: waitResult=9, repeated %u times\n", cNotSupported));
736 cNotSupported = 0;
737 }
738 LogRel(("CLOUD-NET: waitResult=%x\n", waitResult));
739 }
740
741 fRead = false;
742 switch (waitResult)
743 {
744 case ProcessWaitResult_WaitFlagNotSupported:
745 RTThreadYield();
746 /* Fall through */
747 case ProcessWaitResult_StdOut:
748 fRead = true;
749 break;
750 case ProcessWaitResult_Terminate:
751 fDone = true;
752 break;
753 case ProcessWaitResult_Timeout:
754 {
755 ProcessStatus_T enmProcStatus;
756 hrc = guestProcess->COMGETTER(Status)(&enmProcStatus);
757 if (FAILED(hrc))
758 {
759 LogRel(("CLOUD-NET: Failed to query guest process status for 'lgw'. hrc=%x\n", hrc));
760 fDone = true;
761 }
762 else
763 {
764 LogRel(("CLOUD-NET: Guest process timeout for 'lgw'. status=%d\n", enmProcStatus));
765 if ( enmProcStatus == ProcessStatus_TimedOutKilled
766 || enmProcStatus == ProcessStatus_TimedOutAbnormally)
767 fDone = true;
768 }
769 fRead = true;
770 break;
771 }
772 default:
773 LogRel(("CLOUD-NET: Unexpected waitResult=%x\n", waitResult));
774 break;
775 }
776
777 if (fRead)
778 {
779 SafeArray<BYTE> aStdOutData, aStdErrData;
780 hrc = guestProcess->Read(1 /* StdOut */, _64K, 60 * 1000 /* ms */, ComSafeArrayAsOutParam(aStdOutData));
781 if (FAILED(hrc))
782 {
783 LogRel(("CLOUD-NET: Failed to read stdout from guest process for 'lgw'. hrc=%x\n", hrc));
784 break;
785 }
786 hrc = guestProcess->Read(2 /* StdErr */, _64K, 60 * 1000 /* ms */, ComSafeArrayAsOutParam(aStdErrData));
787 if (FAILED(hrc))
788 {
789 LogRel(("CLOUD-NET: Failed to read stderr from guest process for 'lgw'. hrc=%x\n", hrc));
790 break;
791 }
792
793 size_t cbStdOutData = aStdOutData.size();
794 size_t cbStdErrData = aStdErrData.size();
795 if (cbStdOutData == 0 && cbStdErrData == 0)
796 {
797 //LogRel(("CLOUD-NET: Empty output from guest process for 'lgw'. hrc=%x\n", hrc));
798 continue;
799 }
800
801 if (cNotSupported)
802 {
803 LogRel(("CLOUD-NET: waitResult=9, repeated %u times\n", cNotSupported));
804 cNotSupported = 0;
805 }
806 if (cbStdOutData)
807 LogRel(("CLOUD-NET: Got stdout from 'lgw':\n%.*s", aStdOutData.size(), aStdOutData.raw()));
808 if (cbStdErrData)
809 LogRel(("CLOUD-NET: Got stderr from 'lgw':\n%.*s", aStdErrData.size(), aStdErrData.raw()));
810 }
811 }
812 while (!fDone && RTTimeMilliTS() - u64Start < 180 * 1000 /* ms */);
813
814 } while (false);
815
816 return hrc;
817}
818
819
820HRESULT destroyLocalGateway(ComPtr<IVirtualBox> virtualBox, const GatewayInfo& gateways)
821{
822 if (gateways.mGatewayVM.isEmpty())
823 return S_OK;
824
825 LogRel(("CLOUD-NET: Shutting down local gateway '%s'...\n", gateways.mGatewayVM.c_str()));
826
827 ComPtr<IMachine> localGateway;
828 HRESULT hrc = virtualBox->FindMachine(Bstr(gateways.mGatewayVM).raw(), localGateway.asOutParam());
829 if (FAILED(hrc))
830 {
831 LogRel(("CLOUD-NET: Failed to locate '%s'. hrc=%x\n", gateways.mGatewayVM.c_str(), hrc));
832 return hrc;
833 }
834
835 MachineState_T tmp;
836 hrc = localGateway->COMGETTER(State)(&tmp);
837 if (tmp == MachineState_Running)
838 {
839 /* If the gateway VM is running we need to stop it */
840 ComPtr<ISession> session;
841 hrc = session.createInprocObject(CLSID_Session);
842 if (FAILED(hrc))
843 {
844 LogRel(("CLOUD-NET: Failed to create a session. hrc=%x\n", hrc));
845 return hrc;
846 }
847
848 hrc = localGateway->LockMachine(session, LockType_Shared);
849 if (FAILED(hrc))
850 {
851 LogRel(("CLOUD-NET: Failed to lock '%s' for control. hrc=%x\n", gateways.mGatewayVM.c_str(), hrc));
852 return hrc;
853 }
854
855 ComPtr<IConsole> console;
856 hrc = session->COMGETTER(Console)(console.asOutParam());
857 if (FAILED(hrc))
858 {
859 LogRel(("CLOUD-NET: Failed to obtain console for '%s'. hrc=%x\n", gateways.mGatewayVM.c_str(), hrc));
860 return hrc;
861 }
862
863 ComPtr<IProgress> progress;
864 console->PowerDown(progress.asOutParam()); /* We assume the gateway disk to be immutable! */
865
866#if 0
867 hrc = progress->WaitForCompletion(-1);
868 if (FAILED(hrc))
869 LogRel(("CLOUD-NET: Failed to stop '%s'. hrc=%x\n", gateways.mGatewayVM.c_str(), hrc));
870#endif
871 session->UnlockMachine();
872 }
873#if 0
874 /*
875 * Unfortunately we cannot unregister a machine we've just powered down and unlocked.
876 * It takes some time for the machine to unlock completely.
877 */
878 /** @todo Removal of VM should probably be optional in the future. */
879 SafeIfaceArray<IMedium> media;
880 hrc = pLocalGateway->Unregister(CleanupMode_DetachAllReturnHardDisksOnly, ComSafeArrayAsOutParam(media));
881 if (FAILED(hrc))
882 {
883 LogRel(("CLOUD-NET: Failed to unregister '%s'. hrc=%x\n", gateways.mGatewayVM.c_str(), hrc));
884 return hrc;
885 }
886 ComPtr<IProgress> removalProgress;
887 hrc = pLocalGateway->DeleteConfig(ComSafeArrayAsInParam(media), removalProgress.asOutParam());
888 if (FAILED(hrc))
889 {
890 LogRel(("CLOUD-NET: Failed to delete machine files for '%s'. hrc=%x\n", gateways.mGatewayVM.c_str(), hrc));
891 }
892#endif
893 return hrc;
894}
895
896static HRESULT terminateCloudGateway(ComPtr<IVirtualBox> virtualBox, const GatewayInfo& gateways)
897{
898 if (gateways.mGatewayInstanceId.isEmpty())
899 return S_OK;
900
901 LogRel(("CLOUD-NET: Terminating cloud gateway instance '%s'...\n", gateways.mGatewayInstanceId.c_str()));
902
903 HRESULT hrc = S_OK;
904 try {
905 CloudClient client(virtualBox, gateways.mCloudProvider, gateways.mCloudProfile);
906 client.stopCloudGateway(gateways);
907 }
908 catch (CloudError e)
909 {
910 hrc = e.getRc();
911 LogRel(("CLOUD-NET: Failed to terminate cloud gateway instance (rc=%x).\n", hrc));
912 }
913 return hrc;
914}
915
916HRESULT startGateways(ComPtr<IVirtualBox> virtualBox, ComPtr<ICloudNetwork> network, GatewayInfo& gateways)
917{
918 /* Session used to launch and control the local gateway VM */
919 ComPtr<ISession> session;
920 Bstr strNetwork;
921 HRESULT hrc = session.createInprocObject(CLSID_Session);
922 if (FAILED(hrc))
923 LogRel(("CLOUD-NET: Failed to create a session. hrc=%x\n", hrc));
924 else
925 hrc = network->COMGETTER(NetworkName)(strNetwork.asOutParam());
926 if (SUCCEEDED(hrc))
927 hrc = startLocalGateway(virtualBox, session, strNetwork, gateways);
928 if (SUCCEEDED(hrc))
929 hrc = startCloudGateway(virtualBox, network, gateways);
930 if (SUCCEEDED(hrc))
931 hrc = exchangeInfoBetweenGateways(virtualBox, session, gateways);
932
933 session->UnlockMachine();
934 /** @todo Destroy gateways if unsuccessful (cleanup) */
935
936 return hrc;
937}
938
939HRESULT stopGateways(ComPtr<IVirtualBox> virtualBox, const GatewayInfo& gateways)
940{
941 HRESULT hrc = destroyLocalGateway(virtualBox, gateways);
942 AssertComRC(hrc);
943 hrc = terminateCloudGateway(virtualBox, gateways);
944 AssertComRC(hrc);
945 return hrc;
946}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use