Skip to content

feature/hyperv-api-backend: VM & VM Factory Implementation #4080

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 76 commits into
base: feature/hyperv-api-backend-hcs-hcn
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
9fbac27
[hyperv-hcn] remove the "IpConfigurations" section from create endpoi…
xmkg Feb 18, 2025
0874651
[hyperv-api-vm] initial introduction
xmkg Feb 18, 2025
4d748b8
[hyperv-hcs] better handling of get_compute_system_state
xmkg Feb 19, 2025
3c7e204
[gui/platform] add Hyper-V (API) as a backend option
xmkg Feb 24, 2025
237ed91
[.gitignore] add network-cache to gitignore
xmkg Feb 24, 2025
020dfbc
[hyperv-hcs] handle superfluous comma better in scsi and network inte…
xmkg Feb 24, 2025
2b36ce8
[hyperv-api-factory] introduce the factory type
xmkg Feb 27, 2025
2eced8d
[hyperv-api-vm] simplify state management
xmkg Mar 4, 2025
bb9dda6
[hyperv-hcs-api] adapt to std::optional return
xmkg Mar 7, 2025
757a525
[virtual-machine] add fmt formatter for state enum
xmkg Mar 7, 2025
d5523f4
[hyperv-api-vm] update log levels, remove fmt::format from log calls
xmkg Mar 7, 2025
ccc037a
[hyperv-api-vm] more logs & tidyup
xmkg Mar 10, 2025
3482489
[hyperv-api-vm] exceptions: switch to FormattedExceptionBase<>
xmkg Mar 10, 2025
8dabcb6
[hyperv-api-vm] replace factory/vm hyperv_api_ pfx with hcs_
xmkg Mar 10, 2025
9024b35
[hyperv-api-factory] exceptions: switch to FormattedExceptionBase<>
xmkg Mar 10, 2025
98b5f17
[hyperv-api-vm|factory] more grooming
xmkg Mar 10, 2025
e87ce81
[hyperv-api-vm|factory] switch to the log level fns
xmkg Mar 11, 2025
45d4a40
[hyperv-api-vm] Implement Plan9 shares
xmkg Mar 12, 2025
6891aea
[hyperv-api-factory] implement remove_resources_for_impl
xmkg Mar 24, 2025
3be80de
[hyperv-api-factory] implement clone vm functionality
xmkg Mar 24, 2025
f681e99
[hyperv-api-vm] return status of maybe_create_compute_system
xmkg Mar 26, 2025
e1b34c9
[platform_win] Re-implement get_network_interfaces_info natively
xmkg Mar 26, 2025
9ab7a1a
[hyperv-api-factory] implement networks function
xmkg Mar 26, 2025
f516fd0
[rpc] compile with /bigobj
xmkg Apr 9, 2025
36a223f
[platform/win] move wsa init helper & utils to platform_win
xmkg Apr 15, 2025
255d297
[hyperv-api-factory] implement create_bridge_with function
xmkg Apr 15, 2025
f9a37f3
[hyperv-api-factory] ensure bridge(s) are created on vm creation
xmkg Apr 15, 2025
9e2b70d
[hyperv-hcn] overhaul create_network implementation
xmkg Apr 15, 2025
dec1c44
[hyperv-hcn] add "MacAddress" to the endpoint creation parameters
xmkg Apr 15, 2025
e61bdb2
[hyperv-hcn-vm] simplify endpoint create/add parameters setup
xmkg Apr 15, 2025
81ff2b6
[hyperv-hcs] make the linter happy
xmkg Apr 15, 2025
4e3b70a
[hyperv-hcn] unify formatting for HCN data structures
xmkg Apr 16, 2025
6c1d221
[hyperv-api] efforts to make the formatter happy
xmkg Apr 16, 2025
087bdf1
[hyperv-virtdisk] snapshot feature boilerplate
xmkg Apr 21, 2025
db12e83
[base_virtual_machine] add predicate to view_snapshots function
xmkg May 6, 2025
abf7f91
[hyperv-hcs-vm] implement grant_access_to_paths & get_primary_disk_path
xmkg May 6, 2025
c800438
[hyperv-virtdisk-snapshot] implement the snapshot feature
xmkg May 6, 2025
b41a29e
[hyperv-hcs-wrapper] grant_vm_access: use normalized path
xmkg May 6, 2025
157f384
[hyperv-hcs-vm] start: throw exception when start fails
xmkg May 6, 2025
10491f0
[hyperv-api-operation-result] allow cast to std::error_code
xmkg May 6, 2025
3c814a9
[hyperv-virtdisk-snapshot] tidy-up
xmkg May 6, 2025
493bfc8
[file_ops] add std::filesystem::{rename,exists}
xmkg May 6, 2025
e3ba0d1
[hyperv-virtdisk-snapshot] use MP_FILEOPS for filesystem
xmkg May 6, 2025
b155809
[hyperv-virtdisk] fix unit tests
xmkg May 6, 2025
917223e
[hyperv-virtdisk-ut] add unit tests for merge & reparent
xmkg May 7, 2025
276e245
[hyperv-virtdisk-it] add integration tests for merge & reparent
xmkg May 7, 2025
70a2a1d
[hyperv-api] grooming & add more logs
xmkg May 7, 2025
2b26d9d
[hyperv-api] replace string_to_wstring with maybe_widen
xmkg May 7, 2025
9d185fd
[hyperv-api] remove redundant hyperv_api_common{.cpp/.h}
xmkg May 7, 2025
3d35242
[platform_win] remove commented out get_network_interfaces_info fn
xmkg May 7, 2025
215cd45
[hyperv-hcn] move guid_from_string to HCN wrapper
xmkg May 7, 2025
67fd63a
[platform/win] remove guid_to_string functions
xmkg May 7, 2025
d25da6d
[platform/win] comment ip_utils
xmkg May 7, 2025
1453153
[hyperv-hcs] extract SCSI device to its own entity
xmkg May 7, 2025
ba4d72f
[hyperv-api] make the linter happy
xmkg May 7, 2025
9709c3a
[hyperv-hcs] extract network adapter to its own entity
xmkg May 7, 2025
928e280
[hyperv-hcs] replace AddEndpointParameters with HcsNetworkAdapter
xmkg May 7, 2025
84bd096
[hyperv-hcn] rename {add,rm}_endpoint to {add,rm}_network_adapter
xmkg May 7, 2025
bbc9ace
[hyperv-hcs] move plan9 shares formatting to its own source
xmkg May 7, 2025
d54df95
[hyperv-hcs] move CreateComputeSystemParams formatting to src/
xmkg May 7, 2025
19ece86
[hyperv-hcs-hcn] replace fmt::format("{}", v) with fmt::to_string
xmkg May 7, 2025
15fe25b
[hyperv-api-utils] introduce out_ptr type
xmkg May 9, 2025
60e3b61
[hyperv-util] gate void** operator to P != void
xmkg May 9, 2025
b758c02
[hyperv-hcn] use out_ptr where appropriate
xmkg May 9, 2025
4c503b2
[out_ptr] switch to P1132 reference implementation
xmkg May 9, 2025
a196e9c
[hyperv-hcs] introduce modify_compute_system API and HcsRequest
xmkg May 14, 2025
1c410f5
[hyperv-hcs] replace add/remove_network_adapter with modify_cs
xmkg May 14, 2025
5707e3a
[hyperv-hcs] replace resize_memory with modify_compute_system
xmkg May 15, 2025
28ea377
[lint] make the linter happy
xmkg May 15, 2025
80c1d26
[hyperv-hcs] replace 9p API functions with modify_compute_system
xmkg May 16, 2025
db0dad6
[hyperv-hcs] remove update_cpu_count function
xmkg May 16, 2025
c7e3204
[hyperv-hcs] introduce HcsPath type
xmkg May 28, 2025
02665f4
[platform/win] rewrite get_username using native Win32 API
xmkg May 28, 2025
f289bf3
[smb_mount] use Win32 API for folder permission checks
xmkg May 30, 2025
c0df204
[smb_mount] sanitize share name
xmkg May 30, 2025
2de0b22
[hyperv-hcs] use smb mount handler as native mount handler
xmkg May 30, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,4 @@ packaging/windows/custom-actions/x64/*

# clangd cache path
.cache/
**/network-cache
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ if(UNIX)
list(APPEND MULTIPASS_BACKENDS qemu)
endif()

include(src/cmake/cmake-deps.cmake)

# OpenSSL config
find_package(OpenSSL REQUIRED)

Expand Down Expand Up @@ -275,6 +277,7 @@ if(MSVC)
add_definitions(-DLIBSSH_STATIC) # otherwise adds declspec specifiers to libssh apis
add_definitions(-D_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS)
add_definitions(-DWIN32_LEAN_AND_MEAN)
add_definitions(-DSECURITY_WIN32)
set(MULTIPASS_BACKENDS hyperv hyperv_api virtualbox)
set(MULTIPASS_PLATFORM windows)
else()
Expand Down
8 changes: 6 additions & 2 deletions include/multipass/file_ops.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,15 @@ class FileOps : public Singleton<FileOps>
virtual std::unique_ptr<std::istream> open_read(const fs::path& path,
std::ios_base::openmode mode = std::ios_base::in) const;
virtual void copy(const fs::path& src, const fs::path& dist, fs::copy_options copy_options) const;
virtual bool exists(const fs::path& path, std::error_code& err) const;
virtual void rename(const fs::path& old_p, const fs::path& new_p) const;
virtual void rename(const fs::path& old_p, const fs::path& new_p, std::error_code& ec) const noexcept;
virtual bool exists(const fs::path& path) const;
virtual bool exists(const fs::path& path, std::error_code& err) const noexcept;
virtual bool is_directory(const fs::path& path, std::error_code& err) const;
virtual bool create_directory(const fs::path& path, std::error_code& err) const;
virtual bool create_directories(const fs::path& path, std::error_code& err) const;
virtual bool remove(const fs::path& path, std::error_code& err) const;
virtual bool remove(const fs::path& path) const;
virtual bool remove(const fs::path& path, std::error_code& err) const noexcept;
virtual void create_symlink(const fs::path& to, const fs::path& path, std::error_code& err) const;
virtual fs::path read_symlink(const fs::path& path, std::error_code& err) const;
virtual fs::file_status status(const fs::path& path, std::error_code& err) const;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,41 @@
*
*/

#include <hyperv_api/hyperv_api_common.h>
#include <multipass/exceptions/formatted_exception_base.h>
#ifndef MULTIPASS_PLATFORM_WIN_H
#define MULTIPASS_PLATFORM_WIN_H

#include <fmt/xchar.h>
#include <windows.h>

#include <objbase.h> // for CLSIDFromString
#include <fmt/format.h>

#include <codecvt>
#include <locale>
#include <string>

struct WSAData;

namespace multipass::platform
{
struct wsa_init_wrapper
{
wsa_init_wrapper();
~wsa_init_wrapper();

/**
* Check whether WSA initialization has succeeded.
*
* @return true WSA is initialized successfully
* @return false WSA initialization failed
*/
operator bool() const noexcept
{
return wsa_init_result == 0;
}

private:
WSAData* wsa_data{nullptr};
const int wsa_init_result{-1};
};

} // namespace multipass::platform

/**
* Formatter for GUID type
Expand Down Expand Up @@ -63,77 +89,4 @@ struct fmt::formatter<::GUID, Char>
}
};

namespace multipass::hyperv
{

struct GuidParseError : FormattedExceptionBase<>
{
using FormattedExceptionBase<>::FormattedExceptionBase;
};

auto guid_from_wstring(const std::wstring& guid_wstr) -> ::GUID
{
constexpr auto kGUIDLength = 36;
constexpr auto kGUIDLengthWithBraces = kGUIDLength + 2;

const auto input = [&guid_wstr]() {
switch (guid_wstr.length())
{
case kGUIDLength:
// CLSIDFromString requires GUIDs to be wrapped with braces.
return fmt::format(L"{{{}}}", guid_wstr);
case kGUIDLengthWithBraces:
{
if (*guid_wstr.begin() != L'{' || *std::prev(guid_wstr.end()) != L'}')
{
throw GuidParseError{"GUID string either does not start or end with a brace."};
}
return guid_wstr;
}
}
throw GuidParseError{"Invalid length for a GUID string ({}).", guid_wstr.length()};
}();

::GUID guid = {};

const auto result = CLSIDFromString(input.c_str(), &guid);

if (FAILED(result))
{
throw GuidParseError{"Failed to parse the GUID string ({}).", result};
}

return guid;
}

// ---------------------------------------------------------

auto string_to_wstring(const std::string& str) -> std::wstring
{
return std::wstring_convert<std::codecvt_utf8<wchar_t>>().from_bytes(str);
}

// ---------------------------------------------------------

auto guid_from_string(const std::string& guid_str) -> GUID
{
// Just use the wide string overload.
return guid_from_wstring(string_to_wstring(guid_str));
}

// ---------------------------------------------------------

auto guid_to_string(const ::GUID& guid) -> std::string
{

return fmt::format("{}", guid);
}

// ---------------------------------------------------------

auto guid_to_wstring(const ::GUID& guid) -> std::wstring
{
return fmt::format(L"{}", guid);
}

} // namespace multipass::hyperv
#endif // MULTIPASS_PLATFORM_WIN_H
58 changes: 56 additions & 2 deletions include/multipass/virtual_machine.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,15 @@

#include <chrono>
#include <condition_variable>
#include <functional>
#include <memory>
#include <mutex>
#include <optional>
#include <string>
#include <vector>

#include <fmt/format.h>

namespace multipass
{
class MemorySize;
Expand Down Expand Up @@ -78,7 +81,7 @@ class VirtualMachine : private DisabledCopyMove
virtual std::string ssh_hostname()
{
return ssh_hostname(std::chrono::minutes(2));
};
}
virtual std::string ssh_hostname(std::chrono::milliseconds timeout) = 0;
virtual std::string ssh_username() = 0;
virtual std::string management_ipv4() = 0;
Expand All @@ -102,7 +105,8 @@ class VirtualMachine : private DisabledCopyMove
const VMMount& mount) = 0;

using SnapshotVista = std::vector<std::shared_ptr<const Snapshot>>; // using vista to avoid confusion with C++ views
virtual SnapshotVista view_snapshots() const = 0;
using SnapshotPredicate = std::function<bool(const Snapshot&)>;
virtual SnapshotVista view_snapshots(SnapshotPredicate predicate = {}) const = 0;
virtual int get_num_snapshots() const = 0;

virtual std::shared_ptr<const Snapshot> get_snapshot(const std::string& name) const = 0;
Expand Down Expand Up @@ -145,4 +149,54 @@ inline QDir multipass::VirtualMachine::instance_directory() const
return instance_dir; // TODO this should probably only be known at the level of the base VM
}

/**
* Formatter type specialization for CreateComputeSystemParameters
*/
template <typename Char>
struct fmt::formatter<multipass::VirtualMachine::State, Char>
{
constexpr auto parse(basic_format_parse_context<Char>& ctx)
{
return ctx.begin();
}

template <typename FormatContext>
auto format(multipass::VirtualMachine::State state, FormatContext& ctx) const
{
std::string_view v = "(undefined)";
switch (state)
{
case multipass::VirtualMachine::State::off:
v = "off";
break;
case multipass::VirtualMachine::State::stopped:
v = "stopped";
break;
case multipass::VirtualMachine::State::starting:
v = "starting";
break;
case multipass::VirtualMachine::State::restarting:
v = "restarting";
break;
case multipass::VirtualMachine::State::running:
v = "running";
break;
case multipass::VirtualMachine::State::delayed_shutdown:
v = "delayed_shutdown";
break;
case multipass::VirtualMachine::State::suspending:
v = "suspending";
break;
case multipass::VirtualMachine::State::suspended:
v = "suspended";
break;
case multipass::VirtualMachine::State::unknown:
v = "unknown";
break;
}

return format_to(ctx.out(), "{}", v);
}
};

#endif // MULTIPASS_VIRTUAL_MACHINE_H
1 change: 1 addition & 0 deletions src/client/gui/lib/platform/windows.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class WindowsPlatform extends MpPlatform {
Map<String, String> get drivers => const {
'hyperv': 'Hyper-V',
'virtualbox': 'VirtualBox',
'hyperv_api': 'Hyper-V (API)'
};

@override
Expand Down
24 changes: 24 additions & 0 deletions src/cmake/cmake-deps.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Copyright (C) Canonical, Ltd.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

include(FetchContent)

# Declare and fetch fmt
FetchContent_Declare(
out_ptr
GIT_REPOSITORY https://github.com/soasis/out_ptr.git
GIT_TAG 02a577edfcf25e2519e380a95c16743b7e5878a1
)

FetchContent_MakeAvailable(out_ptr)
3 changes: 2 additions & 1 deletion src/platform/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ function(add_target TARGET_NAME)
jsoncpp_static
shared_win
scope_guard
wineventlogger)
wineventlogger
Secur32)
elseif(APPLE)
add_library(${TARGET_NAME} STATIC
platform_osx.cpp
Expand Down
16 changes: 15 additions & 1 deletion src/platform/backends/hyperv_api/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,28 @@ if(WIN32)
endif()

add_library(hyperv_api_backend STATIC
hyperv_api_common.cpp
hcn/hyperv_hcn_api_wrapper.cpp
hcn/hyperv_hcn_route.cpp
hcn/hyperv_hcn_subnet.cpp
hcn/hyperv_hcn_ipam.cpp
hcn/hyperv_hcn_network_policy.cpp
hcs/hyperv_hcs_api_wrapper.cpp
hcs/hyperv_hcs_scsi_device.cpp
hcs/hyperv_hcs_network_adapter.cpp
hcs/hyperv_hcs_plan9_share_params.cpp
hcs/hyperv_hcs_create_compute_system_params.cpp
hcs/hyperv_hcs_request.cpp
hcs/hyperv_hcs_path.cpp
virtdisk/virtdisk_api_wrapper.cpp
virtdisk/virtdisk_snapshot.cpp
hcs_plan9_mount_handler.cpp
hcs_virtual_machine.cpp
hcs_virtual_machine_factory.cpp
)

target_link_libraries(hyperv_api_backend PRIVATE
fmt::fmt-header-only
ztd::out_ptr
utils
computecore.lib
computenetwork.lib
Expand Down
Loading
Loading