blob: 134198778a75f72536e743629b980fcafc2fabfe [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "integration_test_lib.h"
#include <fuchsia/kernel/cpp/fidl.h>
#include <fuchsia/logger/cpp/fidl.h>
#include <fuchsia/net/stack/cpp/fidl.h>
#include <fuchsia/net/virtualization/cpp/fidl.h>
#include <fuchsia/scheduler/cpp/fidl.h>
#include <fuchsia/sysinfo/cpp/fidl.h>
#include <fuchsia/sysmem/cpp/fidl.h>
#include <fuchsia/sysmem2/cpp/fidl.h>
#include <fuchsia/tracing/provider/cpp/fidl.h>
#include <fuchsia/virtualization/cpp/fidl.h>
#include <fuchsia/virtualization/guest/interaction/cpp/fidl.h>
#include <lib/async/cpp/task.h>
#include <lib/syslog/cpp/macros.h>
#include "src/lib/testing/predicates/status.h"
#include "src/virtualization/tests/lib/guest_console.h"
static fit::closure MakeRecurringTask(async_dispatcher_t* dispatcher, fit::closure cb,
zx::duration frequency) {
return [dispatcher, cb = std::move(cb), frequency]() mutable {
cb();
async::PostDelayedTask(dispatcher, MakeRecurringTask(dispatcher, std::move(cb), frequency),
frequency);
};
}
void GuestInteractionTest::GetHostVsockEndpoint(
fidl::InterfaceRequest<::fuchsia::virtualization::HostVsockEndpoint> endpoint) {
std::optional<fuchsia::virtualization::Guest_GetHostVsockEndpoint_Result> vsock_result;
guest_->GetHostVsockEndpoint(
std::move(endpoint),
[&vsock_result](fuchsia::virtualization::Guest_GetHostVsockEndpoint_Result result) {
vsock_result = std::move(result);
});
bool loop_result =
RunLoopWithTimeoutOrUntil([&vsock_result] { return vsock_result.has_value(); }, zx::sec(5));
FX_CHECK(loop_result && vsock_result->is_response());
}
void GuestInteractionTest::SetUp() {
using component_testing::ChildRef;
using component_testing::Directory;
using component_testing::ParentRef;
using component_testing::Protocol;
using component_testing::RealmBuilder;
using component_testing::RealmRoot;
using component_testing::Route;
// Launch the Debian guest
constexpr auto kFakeNetstackComponentName = "fake_netstack";
constexpr auto kDebianGuestManagerUrl = "debian_guest_manager#meta/debian_guest_manager.cm";
constexpr auto kGuestManagerName = "guest_manager";
fuchsia::virtualization::GuestConfig cfg;
cfg.set_virtio_gpu(false);
auto realm_builder = RealmBuilder::Create();
realm_builder.AddChild(kGuestManagerName, kDebianGuestManagerUrl);
realm_builder.AddLocalChild(kFakeNetstackComponentName,
[&]() { return fake_netstack_.NewComponent(); });
realm_builder
.AddRoute(Route{.capabilities =
{
Protocol{fuchsia::logger::LogSink::Name_},
Protocol{fuchsia::kernel::HypervisorResource::Name_},
Protocol{fuchsia::kernel::VmexResource::Name_},
Protocol{fuchsia::sysinfo::SysInfo::Name_},
Protocol{fuchsia::sysmem::Allocator::Name_},
Protocol{fuchsia::sysmem2::Allocator::Name_},
Protocol{fuchsia::tracing::provider::Registry::Name_},
Protocol{fuchsia::scheduler::RoleManager::Name_},
},
.source = {ParentRef()},
.targets = {ChildRef{kGuestManagerName}}})
.AddRoute(Route{.capabilities =
{
Protocol{fuchsia::net::virtualization::Control::Name_},
},
.source = {ChildRef{kFakeNetstackComponentName}},
.targets = {ChildRef{kGuestManagerName}}})
.AddRoute(Route{.capabilities =
{
Protocol{fuchsia::virtualization::DebianGuestManager::Name_},
},
.source = ChildRef{kGuestManagerName},
.targets = {ParentRef()}});
realm_root_ = realm_builder.Build(dispatcher());
fuchsia::virtualization::GuestManager_Launch_Result res;
guest_manager_ =
realm_root_->component().ConnectSync<fuchsia::virtualization::DebianGuestManager>();
FX_LOGS(INFO) << "Starting Debian Guest";
ASSERT_OK(guest_manager_->Launch(std::move(cfg), guest_.NewRequest(), &res));
if (res.is_err()) {
FAIL() << "Failed to launch guest with error: " << static_cast<uint32_t>(res.err());
}
// Start a GuestConsole. When the console starts, it waits until it
// receives some sensible output from the guest to ensure that the guest is
// usable.
FX_LOGS(INFO) << "Getting Serial Console";
guest_.set_error_handler([](zx_status_t status) { FAIL() << zx_status_get_string(status); });
std::optional<fuchsia::virtualization::Guest_GetConsole_Result> get_console_result;
guest_->GetConsole(
[&get_console_result](fuchsia::virtualization::Guest_GetConsole_Result result) {
get_console_result = std::move(result);
});
FX_LOGS(INFO) << "Waiting for Serial Console";
RunLoopUntil([&get_console_result]() { return get_console_result.has_value() || HasFailure(); });
FX_LOGS(INFO) << "Serial Console Received";
if (HasFailure()) {
return;
}
fuchsia::virtualization::Guest_GetConsole_Result& result = get_console_result.value();
switch (result.Which()) {
case fuchsia::virtualization::Guest_GetConsole_Result::Tag::kResponse: {
GuestConsole serial(std::make_unique<ZxSocket>(std::move(result.response().socket)));
ASSERT_OK(serial.Start(zx::time::infinite()));
// Make sure the pty is running and that the guest will receive our commands.
ASSERT_OK(serial.RepeatCommandTillSuccess("echo guest ready", "$", "guest ready",
zx::time::infinite(), zx::sec(1)));
// Wait until guest_interaction_daemon is running.
ASSERT_OK(serial.ExecuteBlocking(
"journalctl -f --no-tail -u guest_interaction_daemon | grep -m1 Listening", "$",
zx::time::infinite(), nullptr));
// Periodically log the guest state.
MakeRecurringTask(
dispatcher(),
[serial = std::move(serial), log_count = 0]() mutable {
ASSERT_OK(
serial.ExecuteBlocking("echo " + std::to_string(++log_count) +
"; journalctl -u guest_interaction_daemon --no-pager",
"$", zx::time::infinite(), nullptr));
},
zx::sec(10))();
break;
}
case fuchsia::virtualization::Guest_GetConsole_Result::Tag::kErr:
FAIL() << "fuchsia.virtualization/Guest.GetConsole error: "
<< static_cast<int32_t>(result.err());
case fuchsia::virtualization::Guest_GetConsole_Result::Tag::Invalid:
FAIL() << "fuchsia.virtualization/Guest.GetConsole: invalid FIDL tag";
}
}
void GuestInteractionTest::TearDown() {
// We're about to shut down the realm; unbind to unhook the error handler.
guest_.Unbind();
bool complete = false;
realm_root_->Teardown([&](fit::result<fuchsia::component::Error> result) { complete = true; });
RunLoopUntil([&]() { return complete; });
}