Packages

Erlang library for supporting OVSDB protocol

Current section

Files

Jump to
ovsdb src ovsdb_vsctl.erl
Raw

src/ovsdb_vsctl.erl

%%-------------------------------------------------------------------
%% Copyright (c) 2020 Vasu Dasari vdasari@gmail.com
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at:
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% @author Vasu Dasari
%% @doc Module providing APIs in lines similar to that of ovs-vsctl utility
%%
%% @end
%% Created : 17. Jun 2020
%%-------------------------------------------------------------------
-module(ovsdb_vsctl).
-author("Vasu Dasari").
-include("ovsdb_client.hrl").
%% API
-export([
set_controller/3, del_controller/2
, add_br/2, del_br/2
, add_port/3, del_port/3
, add_bond/4, del_bond/3
, add_bond_iface/4, del_bond_iface/4
, add_tunnel_port/4, del_tunnel_port/3
]).
-export([vsctl/1, vsctl/2]).
-export([process_vsctl/3, trace/1]).
-export([
]).
-type vsctl_returns() :: ok | error | {ok, term()} | {error, term()}.
-type tunnel_type() :: gre | vxlan.
%% @doc Set Openflow Controller to Bridge
%%
%% This is equivalant to
%% $ ovs-vsctl set-controller br0 tcp:10.1.1.1:6653
-spec set_controller(unicode:chardata(), unicode:chardata(), ovsdb_client:opts()) -> vsctl_returns().
set_controller(BrName, Controller, Opts) ->
vsctl(add_controller, Opts#{controller => Controller, br_name => BrName}).
%% @doc Delete Openflow Controller from Bridge
%%
%% This is equivalant to
%% $ ovs-vsctl del-controller br0
-spec del_controller(unicode:chardata(), ovsdb_client:opts()) -> vsctl_returns().
del_controller(BrName, Opts) ->
vsctl(del_controller, Opts#{br_name => BrName}).
%% @doc Add/Modify a bridge to switch
%%
%% This is quivalent to
%% $ ovs-vsctl --may-exist add-br br1 ...
%% Options Supported:
%% datapath_id
%% datapath_type
%% fail_mode
%% protocols
-spec add_br(unicode:chardata(), ovsdb_client:opts()) -> vsctl_returns().
add_br(BrName, Opts) ->
vsctl(add_br, Opts#{br_name => BrName}).
%% @doc Deletes a bridge to switch
%%
%% This is quivalent to
%% $ ovs-vsctl del-br br1 ...
-spec del_br(unicode:chardata(), ovsdb_client:opts()) -> vsctl_returns().
del_br(BrName, Opts) ->
vsctl(del_br, Opts#{br_name => BrName}).
%% @doc Add/modify port to a bridge
%%
%% This is quivalent to
%% $ ovs-vsctl add-port br1 br1-eth1
%% Options supported:
%% Interface options: admin_state, ofport_request
-spec add_port(unicode:chardata(), unicode:chardata(), ovsdb_client:opts()) -> vsctl_returns().
add_port(BrName, PortName, Opts) ->
vsctl(add_port, Opts#{br_name => BrName, port_name => PortName, iface_list => [PortName]}).
%% @doc Delete port from a bridge
%%
%% This is quivalent to
%% $ ovs-vsctl del-port br1 br1-eth1
-spec del_port(unicode:chardata(), unicode:chardata(), ovsdb_client:opts()) -> vsctl_returns().
del_port(BrName, PortName, Opts) ->
vsctl(del_port, Opts#{br_name => BrName, port_name => PortName, iface_list => [PortName]}).
%% @doc Create or modify bond interface
%%
%% This is quivalent to
%% $ ovs-vsctl --may-exist add-bond br1 br1-bond1 bond1-eth1 bond2-eth2...
%% Options Supported:
%% lacp
%% bond_mode
-spec add_bond(unicode:chardata(), unicode:chardata(), list(), ovsdb_client:opts()) -> ovsdb_client:rpc_return().
add_bond(BrName, BondName, IfaceList, Opts) ->
bond(add_port, BrName, BondName, IfaceList, Opts).
%% @doc Add an interface to a bond
%%
%% This is equivalent to:
%% $ ovs-vsctl add-bond-iface br1 br1-eth1
-spec add_bond_iface(unicode:chardata(), unicode:chardata(),
list() | unicode:chardata(), ovsdb_client:opts()) -> ovsdb_client:rpc_return().
add_bond_iface(BrName, BondName, Iface, Opts) when is_binary(Iface) ->
add_bond_iface(BrName, BondName, [Iface], Opts);
add_bond_iface(BrName, BondName, IfaceList, Opts) ->
bond(add_port, BrName, BondName, IfaceList, Opts).
%% @doc Delete an interface to a bond
%%
%% This is equivalent to:
%% $ ovs-vsctl del-bond-iface br1 br1-eth1
-spec del_bond_iface(unicode:chardata(), unicode:chardata(),
list() | unicode:chardata(), ovsdb_client:opts()) -> ovsdb_client:rpc_return().
del_bond_iface(BrName, BondName, Iface, Opts) when is_binary(Iface) ->
del_bond_iface(BrName, BondName, [Iface], Opts);
del_bond_iface(BrName, BondName, IfaceList, Opts) ->
bond(del_bond_iface, BrName, BondName, IfaceList, Opts).
%% @doc Delete a bond port
%%
%% This is equivalent to:
%% $ ovs-vsctl del-bond br1 br1-bond1
-spec del_bond(unicode:chardata(), unicode:chardata(), ovsdb_client:opts()) -> ovsdb_client:rpc_return().
del_bond(BrName, BondName, Opts) ->
del_port(BrName, BondName, Opts).
bond(Op, BrName, BondName, IfaceList, Opts) ->
vsctl(Op, Opts#{
br_name => BrName, port_name => BondName, iface_list => IfaceList
}).
%% @doc Add/Modify OVS Tunnel port
%%
%% This comand is equivalent to:
%% $ ovs-vsctl add-port br0 a1_b1 -- \
%% set interface a1_b1 type=vxlan options:key=1 options:remote_ip=172.31.1.1
%% Options supported are:
%% key, remote_ip, local_ip, dst_mac, src_mac, vlan_id, out_port
-spec add_tunnel_port(unicode:chardata(), unicode:chardata(), tunnel_type(), ovsdb_client:opts()) -> ovsdb_client:rpc_return().
add_tunnel_port(BrName, Name, TunnelType, Opts) ->
tunnel(add_port, BrName, Name, TunnelType, Opts).
%% @doc Delete a tunnel port
%%
%%
-spec del_tunnel_port(unicode:chardata(), unicode:chardata(), ovsdb_client:opts()) -> ovsdb_client:rpc_return().
del_tunnel_port(BrName, Name, Opts) ->
vsctl(del_port, Opts#{br_name => BrName, port_name => Name, iface_list => [Name]}).
tunnel(Op, BrName, Name, TunnelType, Opts) ->
vsctl(Op, Opts#{
br_name => BrName, port_name => Name, type => TunnelType, iface_list => [Name]
}).
%% @private
vsctl(Cmd) -> vsctl(Cmd, #{}).
vsctl(Op, Opts) ->
try do_vsctl(Op, Opts) of
Return ->
Return
catch
Error:Reason:StackTrace ->
?ERROR("Failed:~n Op ~p, Opts ~p~n Error ~p, Reason ~p~n StackTrace ~n~s",
[Op, Opts, Error, Reason, ovsdb_utils:pretty_print(StackTrace)]),
{error, Error}
end.
%% @private
do_vsctl(Op, #{br_name := BrName} = Opts) when Op == add_controller ->
{ok,[#{<<"rows">> := [#{} = BrMap]}]} =
ovsdb_client:transaction(
ovsdb_ops:select("*", <<"Bridge">>, [{<<"name">>, <<"==">>, BrName}]), Opts),
do_vsctl(Op, #{<<"Bridge">> => BrMap, <<"Controller">> => #{}}, Opts);
do_vsctl(Op, #{br_name := BrName} = Opts) when Op == del_controller ->
{ok,[#{<<"rows">> := [#{<<"controller">> := CtrlrUuId} = BrMap]}]} =
ovsdb_client:transaction(
ovsdb_ops:select("*", <<"Bridge">>, [{<<"name">>, <<"==">>, BrName}]), Opts),
{ok,[#{<<"rows">> := [CtrlrMap]}]} =
ovsdb_client:transaction(
ovsdb_ops:select("*", <<"Controller">>, [{<<"_uuid">>, <<"==">>, CtrlrUuId}]), Opts),
do_vsctl(Op, #{<<"Bridge">> => BrMap, <<"Controller">> => CtrlrMap}, Opts);
do_vsctl(Op, #{br_name := BrName} = Opts) when Op == add_br; Op == del_br ->
BrInfo = case ovsdb_client:transaction(
ovsdb_ops:select("*", <<"Bridge">>, [{<<"name">>, <<"==">>, BrName}]), Opts) of
{ok,[#{<<"rows">> := [B]}]} when is_map(B) ->
B;
_ ->
#{}
end,
do_vsctl(Op, BrInfo, Opts);
do_vsctl(Op, #{br_name := BrName, port_name := PortName} = Opts)
when Op == add_port; Op == del_port; Op == del_bond_iface ->
Request = [
ovsdb_ops:select("*", <<"Bridge">>, [{<<"name">>, <<"==">>, BrName}]),
ovsdb_ops:select("*", <<"Port">>, [{<<"name">>, <<"==">>, PortName}])
] ++ case maps:get(iface_list, Opts, []) of
[] -> [];
IfaceList ->
lists:foldl(fun
(Iface, Acc) ->
[ovsdb_ops:select("*", <<"Interface">>, [{<<"name">>, <<"==">>, Iface}]) | Acc]
end, [], IfaceList)
end,
{ok, Response} = ovsdb_client:transaction(Request, Opts),
DbInfo = lists:foldl(fun
({#{table := Table}, #{<<"rows">> := [Rsp]}}, Acc) when Table == <<"Bridge">>; Table == <<"Port">> ->
Acc#{Table => Rsp};
({#{table := Table, where := [[_,_,Iface]]}, #{<<"rows">> := [Rsp]}}, Acc) ->
OldIfaces = maps:get(Table, Acc, #{}),
Acc#{Table => OldIfaces#{Iface => Rsp}};
(_, Acc) ->
Acc
end, #{<<"Bridge">> => #{}, <<"Port">> => #{}, <<"Interface">> => #{}}, lists:zip(Request, Response)),
case {maps:get(<<"Bridge">>, DbInfo), maps:get(<<"Port">>, DbInfo)} of
{B, _} when (map_size(B) == 0) and (Op == add_port) ->
do_vsctl(add_br, Opts),
do_vsctl(Op, Opts);
{B, P} when ((map_size(B) == 0) or (map_size(P) == 0)) and ((Op == del_port) or (Op == del_bond_iface))->
{error, invalid_configuration};
_ ->
do_vsctl(Op, DbInfo, Opts)
end;
do_vsctl(Cmd, _) ->
?INFO("~p: Not supported", [Cmd]),
not_yet.
%% @private
process_vsctl(Cmd, Opts, State) ->
?INFO("~p: Opts ~p", [Cmd, Opts]),
State.
do_vsctl(Op, #{<<"Bridge">> := BrInfo, <<"Controller">> := CtrlrInfo},
Opts) when Op == add_controller; Op == del_controller ->
controller_cmd(Op, BrInfo, CtrlrInfo, Opts);
do_vsctl(Op, BrInfo, Opts) when Op == add_br; Op == del_br ->
bridge_cmd(Op, BrInfo, Opts);
do_vsctl(Op, #{<<"Bridge">> := BrInfo, <<"Port">> := PortInfo, <<"Interface">> := IfaceList},
Opts) when Op == add_port; Op == del_port ->
port_cmd(Op, BrInfo, PortInfo, IfaceList, Opts);
do_vsctl(Op, #{<<"Bridge">> := BrInfo, <<"Port">> := PortInfo, <<"Interface">> := IfaceList},
Opts) when Op == del_bond_iface ->
bond_cmd(Op, BrInfo, PortInfo, IfaceList, Opts);
do_vsctl(_Op, _BrInfo, _Opts) ->
ok.
controller_cmd(add_controller,
#{<<"controller">> := BrCtrlrUuid},
#{<<"_uuid">> := CtrlUuid}, _) when BrCtrlrUuid == CtrlUuid ->
ok;
controller_cmd(del_controller,
#{<<"controller">> := BrCtrlrUuid} = BrInfo,
#{<<"_uuid">> := CtrlUuid, <<"target">> := Target}, Opts) when BrCtrlrUuid == CtrlUuid ->
Request = [
ovsdb_ops:delete(<<"Controller">>, [{target, '==', Target}]),
ovsdb_ops:update(<<"Bridge">>,
[{<<"_uuid">>, <<"==">>, maps:get(<<"_uuid">>, BrInfo)}],
#{controller => [set, []]}),
ovsdb_ops:mutate(<<"Open_vSwitch">>,
[{<<"_uuid">>, <<"==">>, get_Open_vSwitch_Uuid(Opts)}],
[{<<"next_cfg">>, <<"+=">>, 1}]),
ovsdb_ops:commit(true)
],
transaction(Request, Opts);
controller_cmd(add_controller,
#{<<"controller">> := BrCtrlrUuid} = BrInfo, CtrlrInfo,
#{controller := Target} = Opts) when map_size(CtrlrInfo) == 0 ->
Request =
get_wait_ops(<<"Bridge">>, BrInfo, #{<<"controller">> => BrCtrlrUuid}) ++ [
ovsdb_ops:insert(<<"Controller">>,
#{target => Target}, ovsdb_utils:uuid(controller, Target)),
ovsdb_ops:update(
<<"Bridge">>,
[{<<"_uuid">>, <<"==">>, maps:get(<<"_uuid">>, BrInfo)}],
#{controller => [<<"named-uuid">>, ovsdb_utils:uuid(controller, Target)]}),
ovsdb_ops:mutate(<<"Open_vSwitch">>,
[{<<"_uuid">>, <<"==">>, get_Open_vSwitch_Uuid(Opts)}],
[{<<"next_cfg">>, <<"+=">>, 1}]),
ovsdb_ops:commit(true)
],
transaction(Request, Opts);
controller_cmd(_Op, _BrInfo, _CtrlrInfo, _Opts) ->
{error, invalid_configuration}.
bridge_cmd(del_br, #{<<"_uuid">> := Br_Uuid}, #{br_name := BrName} = Opts) ->
{ok, [#{<<"_uuid">> := Open_vSwitch_Uuid} = OvSInfo]} =
ovsdb_client:dump(<<"Open_vSwitch">>, ['_uuid', bridges]),
{ok, _} = ovsdb_client:transaction([
ovsdb_ops:delete(<<"Bridge">>, [{name, '==', BrName}]),
ovsdb_ops:update(<<"Open_vSwitch">>,
[{<<"_uuid">>, <<"==">> ,Open_vSwitch_Uuid}],
#{bridges => [set, get_set(OvSInfo, <<"bridges">>) -- [Br_Uuid]]}),
ovsdb_ops:mutate(<<"Open_vSwitch">>,
[{<<"_uuid">>, <<"==">> ,Open_vSwitch_Uuid}],
[{<<"next_cfg">>, <<"+=">>, 1}]),
ovsdb_ops:commit(true)
], Opts),
ok;
bridge_cmd(add_br, BrInfo, #{br_name := BrName} = Opts) when map_size(BrInfo) == 0 ->
{ok, [#{<<"_uuid">> := Open_vSwitch_Uuid} = OvSInfo]} =
ovsdb_client:dump(<<"Open_vSwitch">>, ['_uuid', bridges]),
InterfaceOps = ovsdb_ops:insert(<<"Interface">>,
#{name => BrName, type => <<"internal">>},
ovsdb_utils:uuid(interfaces, BrName)),
PortOps = ovsdb_ops:insert(<<"Port">>,
#{name => BrName, interfaces => [<<"named-uuid">>, ovsdb_utils:uuid(interfaces, BrName)]},
ovsdb_utils:uuid(port, BrName)),
BaseBrInfo = #{name => BrName, ports => [<<"named-uuid">>, ovsdb_utils:uuid(port, BrName)]},
NewBrInfo = get_extra_config(bridge, BaseBrInfo, Opts),
BridgeOps = ovsdb_ops:insert(<<"Bridge">>,
maps:merge(NewBrInfo, BaseBrInfo), ovsdb_utils:uuid(bridge, BrName)),
OvS_Ops = ovsdb_ops:update(<<"Open_vSwitch">>,
[{<<"_uuid">>, <<"==">>, Open_vSwitch_Uuid}],
#{bridges => [set, [[<<"named-uuid">>, ovsdb_utils:uuid(bridge, BrName)] | get_set(OvSInfo, <<"bridges">>)]]}),
Request = get_wait_ops(<<"Open_vSwitch">>, OvSInfo, #{<<"bridges">> => ""}) ++
[InterfaceOps, PortOps, BridgeOps, OvS_Ops] ++ [
ovsdb_ops:mutate(<<"Open_vSwitch">>,
[{<<"_uuid">>, <<"==">>, Open_vSwitch_Uuid}],
[{<<"next_cfg">>, <<"+=">>, 1}]),
ovsdb_ops:commit(true)
],
transaction(Request, Opts);
bridge_cmd(add_br, #{<<"_uuid">> := Br_Uuid} = BrInfo, Opts) ->
case get_extra_config(bridge, BrInfo, Opts) of
NewBrInfo when map_size(NewBrInfo) /= 0 ->
BridgeOps = ovsdb_ops:update(<<"Bridge">>,
[{<<"_uuid">>, <<"==">>, Br_Uuid}], NewBrInfo),
Request = [BridgeOps] ++ [
ovsdb_ops:mutate(<<"Open_vSwitch">>,
[{<<"_uuid">>, <<"==">>, get_Open_vSwitch_Uuid(Opts)}],
[{<<"next_cfg">>, <<"+=">>, 1}]),
ovsdb_ops:commit(true)
],
transaction(Request, Opts);
_ ->
ok
end;
bridge_cmd(_Op, _PortInfo, _Opts) ->
?INFO("Unhandled ~p", [{_Op, _PortInfo, _Opts}]),
ok.
port_cmd(add_port,
#{<<"_uuid">> := Br_Uuid} = BrInfo,
PortInfo, IfaceInfo,
#{port_name := PortName, iface_list := IfaceList} = Opts) when map_size(PortInfo) == 0 ->
#{ops := IfaceOps, uuids := IfaceUuids} = lists:foldl(fun
(Iface, #{ops := OpsAcc, uuids := UuidsAcc} = Acc) ->
case maps:get(Iface, IfaceInfo, #{}) of
R when map_size(R) == 0 ->
ExtraConfig = get_extra_config(interface, #{}, Opts),
Acc#{
ops => [
ovsdb_ops:insert(<<"Interface">>,
ExtraConfig#{name => Iface},
ovsdb_utils:uuid(interface, Iface))
| OpsAcc],
uuids => [[<<"named-uuid">>, ovsdb_utils:uuid(interface, Iface)] | UuidsAcc]
};
_ ->
Acc
end
end, #{ops => [], uuids => []}, IfaceList),
PortOps = case IfaceOps /= [] of
true when map_size(PortInfo) == 0 ->
ExtraConfig = get_extra_config(port, #{}, Opts),
PortInsert = ovsdb_ops:insert(<<"Port">>,
ExtraConfig#{name => PortName,
interfaces => [set, IfaceUuids]
}, ovsdb_utils:uuid(port, PortName)),
BridgeUpdate = ovsdb_ops:update(
<<"Bridge">>,
[{<<"_uuid">>, <<"==">>, Br_Uuid}],
#{ports => [set, [[<<"named-uuid">>, ovsdb_utils:uuid(port, PortName)] | get_set(BrInfo, <<"ports">>)]]}
),
[PortInsert, BridgeUpdate];
true ->
[ovsdb_ops:update(
<<"Port">>,
[{<<"_uuid">>, <<"==">>, maps:get(<<"_uuid">>, PortInfo)}],
#{interfaces => [set, IfaceUuids ++ get_set(PortInfo, <<"interfaces">>)]})];
_ ->
[]
end,
case PortOps == [] of
true ->
ok;
_ ->
Request = get_wait_ops(<<"Bridge">>, BrInfo, #{<<"ports">> => ""}) ++
IfaceOps ++ PortOps ++ [
ovsdb_ops:mutate(<<"Open_vSwitch">>,
[{<<"_uuid">>, <<"==">>, get_Open_vSwitch_Uuid(Opts)}],
[{<<"next_cfg">>, <<"+=">>, 1}]),
ovsdb_ops:commit(true)
],
transaction(Request, Opts)
end;
port_cmd(add_port, _BrInfo,
#{<<"_uuid">> := Port_Uuid} = PortInfo,
IfaceInfo, #{iface_list := IfaceList} = Opts) ->
IfaceOps = lists:foldl(fun
(IfName, Acc) ->
case maps:get(IfName, IfaceInfo, #{}) of
#{<<"_uuid">> := If_Uuid} = IfaceMap ->
case get_extra_config(interface, IfaceMap, Opts) of
NewIfInfo when map_size(NewIfInfo) /= 0 ->
[ovsdb_ops:update(<<"Interface">>,
[{<<"_uuid">>, <<"==">>, If_Uuid}], NewIfInfo)];
_ ->
[]
end;
_ ->
IfUuid = ovsdb_utils:uuid(interface, IfName),
NewIfMap = get_extra_config(interface, #{}, Opts),
[
ovsdb_ops:insert(<<"Interface">>, NewIfMap#{name => IfName}, IfUuid),
ovsdb_ops:update(
<<"Port">>,
[{<<"_uuid">>, <<"==">>, maps:get(<<"_uuid">>, PortInfo)}],
#{interfaces => [set, [[<<"named-uuid">>, IfUuid] | get_set(PortInfo, <<"interfaces">>)]]})
]
end ++ Acc
end, [], IfaceList),
PortOps = case get_extra_config(port, PortInfo, Opts) of
NewPortInfo when map_size(NewPortInfo) /= 0 ->
[ovsdb_ops:update(<<"Port">>,
[{<<"_uuid">>, <<"==">>, Port_Uuid}], NewPortInfo)];
_ ->
[]
end,
case PortOps ++ IfaceOps of
[] ->
ok;
Ops ->
Request = Ops ++ [
ovsdb_ops:mutate(<<"Open_vSwitch">>,
[{<<"_uuid">>, <<"==">>, get_Open_vSwitch_Uuid(Opts)}],
[{<<"next_cfg">>, <<"+=">>, 1}]),
ovsdb_ops:commit(true)
],
transaction(Request, Opts)
end;
port_cmd(Op,
#{<<"_uuid">> := Br_Uuid, <<"ports">> := [<<"set">>, Ports]},
#{<<"_uuid">> := Port_Uuid}, _,
#{port_name := PortName} = Opts) when Op == del_port; Op == del_bond ->
{ok,_} = ovsdb_client:transaction([
ovsdb_ops:delete(<<"Port">>, [{name, '==', PortName}]),
ovsdb_ops:update(<<"Bridge">>,
[{<<"_uuid">>, <<"==">> ,Br_Uuid}],
#{ports => [set, Ports -- [Port_Uuid]]}),
ovsdb_ops:mutate(<<"Open_vSwitch">>,
[{<<"_uuid">>, <<"==">> ,get_Open_vSwitch_Uuid(Opts)}],
[{<<"next_cfg">>, <<"+=">>, 1}]),
ovsdb_ops:commit(true)
], Opts),
ok;
port_cmd(_Op, _BrInfo, _PortInfo, _, _Opts) ->
?INFO("Unhandled ~p", [{_Op, _BrInfo, _PortInfo, _Opts}]),
ok.
bond_cmd(del_bond_iface, _, PortInfo, IfaceInfo, Opts) when is_map(PortInfo) ->
#{ops := IfaceOps, uuids := IfaceUuids} = maps:fold(fun
(Iface, #{<<"_uuid">> := Uuid}, #{ops := OpsAcc, uuids := UuidsAcc} = Acc) ->
Acc#{
ops => [ovsdb_ops:delete(<<"Interface">>, [{name, '==', Iface}]) | OpsAcc],
uuids => [Uuid | UuidsAcc]
}
end, #{ops => [], uuids => []}, IfaceInfo),
PortOps = case IfaceOps /= [] of
true when map_size(PortInfo) == 0 ->
[];
true ->
[ovsdb_ops:update(
<<"Port">>,
[{<<"_uuid">>, <<"==">>, maps:get(<<"_uuid">>, PortInfo)}],
#{interfaces => [set, get_set(PortInfo, <<"interfaces">>) -- IfaceUuids]})];
_ ->
[]
end,
case PortOps == [] of
true ->
ok;
_ ->
Request = IfaceOps ++ PortOps ++ [
ovsdb_ops:mutate(<<"Open_vSwitch">>,
[{<<"_uuid">>, <<"==">>, get_Open_vSwitch_Uuid(Opts)}],
[{<<"next_cfg">>, <<"+=">>, 1}]),
ovsdb_ops:commit(true)
],
transaction(Request, Opts)
end;
bond_cmd(_Op, _BrInfo, _PortInfo, _IfaceList, _Opts) ->
?INFO("bond_cmd: Unhandled~n~s", [ovsdb_utils:pretty_print({_Op, _BrInfo, _PortInfo, _IfaceList, _Opts})]),
ok.
transaction(Request, Opts) ->
{ok,Response} = ovsdb_client:transaction(Request, Opts),
?DEBUG("Request~n~s", [ovsdb_utils:pretty_print(Request)]),
?DEBUG("Response~n~s", [ovsdb_utils:pretty_print(Response)]),
lists:foldl(fun
({Req, #{<<"error">> := _} = Rsp}, _) ->
?WARNING("Errors:~n~s", [ovsdb_utils:pretty_print({Req, Rsp})]),
{error, [Req, Rsp]};
(_, Acc) ->
Acc
end, ok, lists:zip(Request, Response)).
get_Open_vSwitch_Uuid(Opts) ->
{ok, [#{<<"_uuid">> := Open_vSwitch_Uuid}]} =
ovsdb_client:dump(<<"Open_vSwitch">>, ['_uuid'], Opts),
Open_vSwitch_Uuid.
trace(on) ->
dbg:start(),
dbg:tracer(),
dbg:p(all, c)
,dbg:tpl(?MODULE, [])
;
trace(off) ->
dbg:stop().
get_set(Map, Key) ->
case maps:get(Key, Map, not_found) of
[<<"set">>, Value] ->
Value;
not_found ->
[];
Value ->
[Value]
end.
get_extra_config(bridge, BrInfo, Opts) ->
maps:fold(fun
(Key, Value, Acc) when Key == datapath_id ->
get_extra_other_config(<<"other_config">>, Key, Value, Acc);
(Key, Value, Acc) when
Key == datapath_type;
Key == fail_mode
->
get_extra_config(BrInfo, Key, Value, Acc);
(Key, Value, Acc) when
Key == protocols
->
get_extra_config(BrInfo, Key, Value, Acc);
(_,_, Acc) ->
Acc
end, #{}, Opts);
get_extra_config(interface, BrInfo, Opts) ->
maps:fold(fun
(Key, Value, Acc) when
Key == key;
Key == remote_ip;
Key == local_ip;
Key == dst_mac;
Key == src_mac;
Key == vlan_id;
Key == out_port
->
%% key, remote_ip, local_ip, dst_mac, src_mac, vlan_id, out_port
get_extra_other_config(<<"options">>, Key, Value, Acc);
(Key, Value, Acc) when
Key == admin_state;
Key == ofport_request;
Key == type
->
get_extra_config(BrInfo, Key, Value, Acc);
(_,_, Acc) ->
Acc
end, #{}, Opts);
get_extra_config(port, BrInfo, Opts) ->
maps:fold(fun
(Key, Value, Acc) when
Key == bond_mode;
Key == lacp
->
get_extra_config(BrInfo, Key, Value, Acc);
(_,_, Acc) ->
Acc
end, #{}, Opts).
get_extra_config(DbMap, Key, Value, Opts) ->
Column = ovsdb_utils:to_binstring(Key),
case maps:get(Column, DbMap, not_found) of
V when V =:= Value ->
Opts;
_ when is_list(Value)->
Opts#{Column => [set, Value]};
_ ->
Opts#{Column => Value}
end.
get_extra_other_config(OtherKey, Key, Value, Opts) ->
[map, OldPairs] = maps:get(OtherKey, Opts, [map, []]),
Opts#{OtherKey => [map, [[ovsdb_utils:to_binstring(Key), Value] | OldPairs]]}.
get_wait_ops(Tabls, DbMap, NewMap) ->
maps:fold(fun
(Key, _Value, Acc) ->
[ovsdb_ops:wait(Tabls,
[{<<"_uuid">>, '==', maps:get(<<"_uuid">>, DbMap)}],
[#{Key => [set, get_set(DbMap, Key)]}],
'==',
[Key]
) | Acc]
end, [], NewMap).