Skip to content

Commit

Permalink
virtuerl: more steps towards a MVP
Browse files Browse the repository at this point in the history
  • Loading branch information
verbit committed Feb 10, 2024
1 parent dc062c7 commit 8fc0b7e
Show file tree
Hide file tree
Showing 7 changed files with 348 additions and 158 deletions.
10 changes: 10 additions & 0 deletions virtuerl/src/virtuerl_ipam.erl
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
-behavior(gen_server).

-export([req/1, init/1, handle_call/3, subnet/1, get_range/1, get_next/6, terminate/2, ipam_next_ip/1, start_server/1, stop_server/1, ipam_put_net/1, start_link/0, handle_cast/2, ipam_delete_net/1, ipam_create_net/1, ipam_list_nets/0, ipam_get_net/1, ipam_put_ip/3, assign_next/3]).
-export([unassign/1]).

-include_lib("khepri/include/khepri.hrl").
-include_lib("khepri/src/khepri_error.hrl").
Expand Down Expand Up @@ -51,6 +52,8 @@ ipam_put_net(NetworkDef) ->
ipam_list_nets() ->
gen_server:call(ipam, net_list).

ipam_delete_net(ID) when is_list(ID) ->
ipam_delete_net(list_to_binary(ID));
ipam_delete_net(ID) ->
case gen_server:call(ipam, {net_delete, ID}) of
{ok, Res} ->
Expand Down Expand Up @@ -82,6 +85,9 @@ assign_next(NetworkID, Tag, VMID) ->
Other
end.

unassign(DomainId) ->
gen_server:call(ipam, {unassign, DomainId}).

ipam_next_ip(NetworkName) ->
case gen_server:call(ipam, {ip_next, NetworkName}) of
{ok, Res} ->
Expand Down Expand Up @@ -202,6 +208,10 @@ handle_call({ip_delete, NetworkId, IpAddr}, _From, StoreId) ->
R = khepri:delete(StoreId, [network, NetworkId, ?KHEPRI_WILDCARD_STAR, IpAddr]),
{reply, R, StoreId};

handle_call({unassign, DomainId}, _From, StoreId) ->
ok = khepri:delete_many(StoreId, [network, ?KHEPRI_WILDCARD_STAR, ?KHEPRI_WILDCARD_STAR, #if_data_matches{pattern = DomainId}]),
{reply, ok, StoreId};

handle_call({ip_clear}, _From, StoreId) ->
R = khepri:delete(StoreId, [network]),
{reply, R, StoreId}.
Expand Down
17 changes: 10 additions & 7 deletions virtuerl/src/virtuerl_mgt.erl
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ init([]) ->
handle_continue(setup_base, State) ->
ok = filelib:ensure_path(filename:join(home_path(), "domains")),

%% BaseImagePath = filename:join(home_path(), "debian-12-genericcloud-amd64-20230910-1499.qcow2"),
BaseImagePath = filename:join(home_path(), "openSUSE-Leap-15.5.x86_64-NoCloud.qcow2"),
BaseImagePath = filename:join(home_path(), "debian-12-genericcloud-amd64-20230910-1499.qcow2"),
%% BaseImagePath = filename:join(home_path(), "openSUSE-Leap-15.5.x86_64-NoCloud.qcow2"),
case filelib:is_regular(BaseImagePath) of
true -> ok;
false ->
Expand Down Expand Up @@ -111,7 +111,7 @@ generate_unique_tap_name(TapNames) ->
handle_call({domain_create, Conf}, _From, State) ->
{Table} = State,
DomainID = virtuerl_util:uuid4(),
Domain = maps:merge(#{id => DomainID, name => DomainID}, Conf), % TODO: save ipv4 addr as well
Domain = maps:merge(#{id => DomainID, name => DomainID, vcpu => 1, memory => 512}, Conf), % TODO: save ipv4 addr as well
dets:insert_new(Table, {DomainID, Domain}),
dets:sync(Table),

Expand Down Expand Up @@ -182,24 +182,27 @@ handle_call({domain_update, #{id := DomainID, state := RunState}}, _From, {Table
handle_call(domains_list, _From, State) ->
{Table} = State,
Domains = dets:match_object(Table, '_'),
{reply, [maps:merge(#{state => stopped, name => Id}, Domain) || {Id, Domain} <- Domains], State};
{reply, [maps:merge(#{state => running, name => Id, vcpu => 1, memory => 512}, Domain) || {Id, Domain} <- Domains], State};
handle_call({domain_get, #{id := DomainID}}, _From, State) ->
{Table} = State,
Reply = case dets:lookup(Table, DomainID) of
[{_, #{mac_addr := MacAddr, ipv4_addr:=IP, tap_name := TapName} = Domain}] ->
DomRet = Domain#{mac_addr := binary:encode_hex(MacAddr), ipv4_addr := virtuerl_net:format_ip_bitstring(IP), tap_name := iolist_to_binary(TapName)},
{ok, maps:merge(#{name => DomainID}, DomRet)};
{ok, maps:merge(#{state => running, name => DomainID, vcpu => 1, memory => 512}, DomRet)};
[] -> notfound
end,
{reply, Reply, State};
handle_call({domain_delete, #{id := DomainID}}, _From, State) ->
{Table} = State,
ok = virtuerl_ipam:unassign(DomainID),
Res = dets:delete(Table, DomainID),
dets:sync(Table),
io:format("terminating ~p~n", [DomainID]),
ok = supervisor:terminate_child(virtuerl_sup, DomainID),
supervisor:terminate_child(virtuerl_sup, DomainID),
io:format("done terminating ~p~n", [DomainID]),
ok = supervisor:delete_child(virtuerl_sup, DomainID),
supervisor:delete_child(virtuerl_sup, DomainID),
DomainHomePath = filename:join([virtuerl_mgt:home_path(), "domains", DomainID]),
file:del_dir_r(DomainHomePath),
ok = gen_server:call(virtuerl_net, {net_update}),
{reply, Res, State}.

Expand Down
6 changes: 3 additions & 3 deletions virtuerl/src/virtuerl_net.erl
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ update_nftables(Domains) ->

" chain postrouting {\n",
" type nat hook postrouting priority -5; policy accept;\n",
" oifname != \"verlbr*\" iifname \"verlbr*\" masquerade\n",
" oifname != \"verlbr*\" iifname \"verlbr*\" masquerade\n", % TODO: we need to base it on IPs
" }\n",
"}\n"
],
Expand Down Expand Up @@ -258,7 +258,7 @@ generate_unique_bridge_name(Ifnames) ->
generate_unique_bridge_name(Ifnames)
end.

to_vtap_map(MacAddr) ->
to_vtap_mac(MacAddr) ->
<<A:5, _:1, 2:2, B:40>> = MacAddr,
<<A:5, 0:1, 2:2, B:40>>.

Expand All @@ -274,7 +274,7 @@ sync_taps(Domains) ->
TapsTarget = sets:from_list([iolist_to_binary(TapName) || {_, #{tap_name := TapName}} <- Domains]), % TODO: persist tap_name as binary
io:format("Taps Target: ~p~n", [sets:to_list(TapsTarget)]),
io:format("Taps Actual: ~p~n", [sets:to_list(TapsActual)]),
TapsMap = maps:from_list([{iolist_to_binary(Tap), {to_vtap_map(MacAddr), network_cidrs_to_bride_cidrs(Cidrs)}} || {_, #{network_addrs := Cidrs, tap_name := Tap, mac_addr := MacAddr}} <- Domains]),
TapsMap = maps:from_list([{iolist_to_binary(Tap), {to_vtap_mac(MacAddr), network_cidrs_to_bride_cidrs(Cidrs)}} || {_, #{network_addrs := Cidrs, tap_name := Tap, mac_addr := MacAddr}} <- Domains]),
io:format("TapsMap: ~p~n", [TapsMap]),

TapsToDelete = sets:subtract(TapsActual, TapsTarget),
Expand Down
18 changes: 11 additions & 7 deletions virtuerl/src/virtuerl_qemu.erl
Original file line number Diff line number Diff line change
Expand Up @@ -39,22 +39,24 @@ callback_mode() ->
init([ID]) ->
{ok, Table} = dets:open_file(domains, [{file, filename:join(virtuerl_mgt:home_path(), "domains.dets")}]),
[{DomainId, #{mac_addr:=MacAddr, tap_name := TapName} = DomainRaw}] = dets:lookup(Table, ID),
Domain = maps:merge(#{user_data => ""}, DomainRaw),
Domain = maps:merge(#{user_data => "", vcpu => 1, memory => 512}, DomainRaw),
DomainHomePath = filename:join([virtuerl_mgt:home_path(), "domains", DomainId]),
ok = filelib:ensure_path(DomainHomePath),
RootVolumePath = filename:join(DomainHomePath, "root.qcow2"),
case filelib:is_regular(RootVolumePath) of
false ->
%% BaseImagePath = filename:join([virtuerl_mgt:home_path(), "debian-12-genericcloud-amd64-20230910-1499.qcow2"]),
BaseImagePath = filename:join([virtuerl_mgt:home_path(), "openSUSE-Leap-15.5.x86_64-NoCloud.qcow2"]),
exec:run(lists:flatten(io_lib:format("qemu-img create -f qcow2 -b ~s -F qcow2 ~s", [filename:absname(BaseImagePath), RootVolumePath])), [sync]);
BaseImagePath = filename:join([virtuerl_mgt:home_path(), "debian-12-genericcloud-amd64-20230910-1499.qcow2"]),
%% BaseImagePath = filename:join([virtuerl_mgt:home_path(), "openSUSE-Leap-15.5.x86_64-NoCloud.qcow2"]),
exec:run(lists:flatten(io_lib:format("qemu-img create -f qcow2 -b ~s -F qcow2 ~s 20G", [filename:absname(BaseImagePath), RootVolumePath])), [sync]);
_ -> noop
end,
process_flag(trap_exit, true),
ensure_cloud_config(Domain),
file:delete(filename:join(DomainHomePath, "qmp.sock")),
file:delete(filename:join(DomainHomePath, "serial.sock")),
Cmd = iolist_to_binary(["kvm -no-shutdown -S -nic tap,ifname=",TapName,",script=no,downscript=no,model=virtio-net-pci,mac=",virtuerl_util:mac_to_str(MacAddr), " -vnc :1 -display none -serial none -m 512 -drive file=root.qcow2,if=virtio -drive driver=raw,file=cloud_config.iso,if=virtio -qmp unix:qmp.sock,server=on,wait=off"]), % -serial unix:serial.sock,server=on,wait=off
file:delete(filename:join(DomainHomePath, "vnc.sock")),
#{vcpu := Vcpu, memory := Memory} = Domain,
Cmd = iolist_to_binary(["kvm -no-shutdown -S -nic tap,ifname=",TapName,",script=no,downscript=no,model=virtio-net-pci,mac=",virtuerl_util:mac_to_str(MacAddr), " -vnc unix:vnc.sock -display none -serial none -smp ",integer_to_binary(Vcpu)," -m ",integer_to_binary(Memory)," -drive file=root.qcow2,if=virtio -drive driver=raw,file=cloud_config.iso,if=virtio -qmp unix:qmp.sock,server=on,wait=off"]), % -serial unix:serial.sock,server=on,wait=off
io:format("QEMU cmdline: ~s~n", [Cmd]),
{ok, Pid, OsPid} = exec:run_link(Cmd, [{cd, DomainHomePath}]),
State = #{table => Table, id => ID, domain => Domain, qemu_pid => {Pid, OsPid}, qmp_pid => undefined},
Expand Down Expand Up @@ -179,7 +181,7 @@ ensure_cloud_config(#{id := DomainID} = Domain) ->
false -> create_cloud_config(Domain)
end.

create_cloud_config(#{id := DomainID, mac_addr := MacAddr, cidrs := Cidrs, user_data := UserData}) ->
create_cloud_config(#{id := DomainID, name := DomainName, mac_addr := MacAddr, cidrs := Cidrs, user_data := UserData}) ->
NetConf = [
"version: 2\n",
"ethernets:\n",
Expand All @@ -189,6 +191,8 @@ create_cloud_config(#{id := DomainID, mac_addr := MacAddr, cidrs := Cidrs, user_
" set-name: ens2\n",
" dhcp4: false\n",
" dhcp6: false\n",
" nameservers:\n",
" addresses: [8.8.8.8, 8.8.4.4]\n",
" addresses:\n", [[
" - ", virtuerl_net:format_ip(IpAddr), "/", integer_to_binary(Prefixlen), "\n"] || {IpAddr, Prefixlen} <- Cidrs],
" routes:\n", [[
Expand All @@ -198,7 +202,7 @@ create_cloud_config(#{id := DomainID, mac_addr := MacAddr, cidrs := Cidrs, user_
],
MetaData = [
"instance-id: ", DomainID, "\n",
"local-hostname: ", DomainID, "\n"
"local-hostname: ", DomainName, "\n"
],
DomainBasePath = filename:join([virtuerl_mgt:home_path(), "domains", DomainID]),
IsoBasePath = filename:join(DomainBasePath, "iso"),
Expand Down
Loading

0 comments on commit 8fc0b7e

Please sign in to comment.