diff --git a/bin/enum_gen b/bin/enum_gen index 1d28df9..96c92f5 100755 Binary files a/bin/enum_gen and b/bin/enum_gen differ diff --git a/lib/ovsdb/open_vswitch.ex b/lib/ovsdb/open_vswitch.ex index 43b15ca..725da12 100644 --- a/lib/ovsdb/open_vswitch.ex +++ b/lib/ovsdb/open_vswitch.ex @@ -16,28 +16,43 @@ defmodule OVSDB.OpenvSwitch do @controller "Controller" @bridge "Bridge" - def start_link(server) do - GenServer.start_link(__MODULE__, [server]) - end - def find_by_name(pid, table, name) do GenServer.call(pid, {:find_by_name, table, name}) end - def add_br(pid, bridge, options \\ []) do + # API functions + + @doc """ + Create a bridge: + + ## options: + iex> OpenvSwitch.add_br( + client_pid, + name: "br0", + fail_mode: "standalone", + datapath_id: "0000000000000001", + disable_in_band: "true" + ) + """ + def add_br(pid, [_|_] = options) do br_uuids = GenServer.call(pid, {:sync_get, @open_vswitch, "bridges"}) add_br_options = [ - bridge: bridge, - controller: options[:controller] || "tcp:127.0.0.1:6653", - protocol: options[:protocol] || "OpenFlow13", - fail_mode: options[:fail_mode] || "standalone", + bridge: options[:name], + fail_mode: options[:fail_mode] || "standalone", + datapath_id: options[:datapath_id], + disable_in_band: options[:disable_in_band], br_uuids: br_uuids ] GenServer.call(pid, {:add_br, add_br_options}) end + @doc """ + Delete a bridge + + iex> OpenvSwitch.del_br(client_pid, "br0") + """ def del_br(pid, bridge) do case find_by_name(pid, @bridge, bridge) do %{"_uuid" => uuid} -> @@ -55,6 +70,109 @@ defmodule OVSDB.OpenvSwitch do end end + @doc """ + Set a controller to the switch + + iex> OpenvSwitch.set_controller( + client_pid, + bridge: "br0", + target: "tcp:127.0.0.1:6653", + connection_mode: "out-of-band", + controller_rate_limit: 100, + controller_burst_limit: 25, + protocol: "OpenFlow13" + ) + """ + def set_controller(pid, [_|_] = options) do + case find_by_name(pid, @bridge, options[:bridge]) do + :not_found -> + {:error, :not_found} + %{"_uuid" => _uuid} -> + set_ctl_opts = [ + bridge: options[:bridge], + target: options[:target] || "tcp:127.0.0.1:6653", + connection_mode: options[:connection_mode] || "out-of-band", + controller_rate_limit: options[:controller_rate_limit] || 1000, + controller_burst_limit: options[:controller_burst_limit] || 100, + protocol: options[:protocol] || "OpenFlow13", + ] + + GenServer.call(pid, {:set_controller, set_ctl_opts}) + end + end + + @doc """ + iex> OpenvSwitch.add_port( + client_pid, + bridge: "br0", + name: "vxlan5", + type: "vxlan", + remote_ip: "flow", + ofport_request: 99 + ) + """ + def add_port(pid, [_|_] = options) do + case find_by_name(pid, @bridge, options[:bridge]) do + :not_found -> + {:error, :not_found} + %{"_uuid" => _uuid, "ports" => ports} -> + port_opts = [ + bridge: options[:bridge], + name: options[:name], + type: options[:type] || "system", + ofport_request: options[:ofport_request], + remote_ip: options[:remote_ip], + local_ip: options[:local_ip], + in_key: options[:in_key], + out_key: options[:out_key], + key: options[:key], + tos: options[:tos], + ttl: options[:ttl], + df_default: options[:df_default], + csum: options[:csum], + peer_cert: options[:peer_cert], + certificate: options[:certificate], + private_key: options[:private_key], + psk: options[:psk], + ingress_policing_rate: options[:ingress_policing_rate] || 0, + ingress_policing_burst: options[:ingress_policing_burst] || 0, + ports: ports + ] + + GenServer.call(pid, {:add_port, port_opts}) + end + end + + @doc """ + iex> OpenvSwitch.del_port(client_pid, bridge: "br0", port: "vxlan5") + """ + def del_port(pid, [_|_] = options) do + case find_by_name(pid, @bridge, options[:bridge]) do + :not_found -> + {:error, :not_found} + %{"_uuid" => _uuid, "ports" => ports} -> + case find_by_name(pid, @port, options[:port]) do + :not_found -> + {:error, :not_found} + %{"_uuid" => port_uuid} -> + new_ports = del_elem_from_set(ports, port_uuid) + + port_opts = [ + bridge: options[:bridge], + ports: new_ports + ] + + GenServer.call(pid, {:del_port, port_opts}) + end + end + end + + # GenServer callback functions + + def start_link(server) do + GenServer.start_link(__MODULE__, [server]) + end + def init([server]) do state = server @@ -77,25 +195,24 @@ defmodule OVSDB.OpenvSwitch do %State{client_pid: pid, ovs_uuid: ovs} = state br_iface = %{name: options[:bridge], type: :internal} br_port = %{name: options[:bridge], interfaces: ["named-uuid", "brinterface"]} - controller = %{target: options[:controller]} + + other_config = [ + ["datapath-id", options[:datapath_id]], + ["dp-desc", options[:dp_desc]], + ["disable-in-band", options[:disable_in_band]], + ["in-band-queue", options[:in_band_queue]] + ] new_bridge = %{ name: options[:bridge], ports: ["named-uuid", "brport"], - controller: ["named-uuid", "brcontroller"], fail_mode: options[:fail_mode], - protocols: options[:protocol] + other_config: make_ovsdb_map(other_config) } named_uuid = ["named-uuid", "bridge"] - new_bridges = - case options[:br_uuids] do - ["set", []] -> %{bridges: named_uuid} - ["set", bridges] -> %{bridges: ["set", bridges ++ [named_uuid]]} - ["uuid", _] = bridge -> %{bridges: ["set", [bridge] ++ [named_uuid]]} - end - + new_bridges = add_elem_to_set(options[:br_uuids], named_uuid) next_config = [{"next_cfg", "+=", 1}] eq_ovs_uuid = [{"_uuid", "==", ovs}] @@ -104,7 +221,6 @@ defmodule OVSDB.OpenvSwitch do [ :eovsdb_op.insert(@interface, br_iface, "brinterface"), :eovsdb_op.insert(@port, br_port, "brport"), - :eovsdb_op.insert(@controller, controller, "brcontroller"), :eovsdb_op.insert(@bridge, new_bridge, "bridge"), :eovsdb_op.update(@open_vswitch, eq_ovs_uuid, new_bridges), :eovsdb_op.mutate(@open_vswitch, eq_ovs_uuid, next_config) @@ -132,16 +248,103 @@ defmodule OVSDB.OpenvSwitch do {:reply, replies, state} end + def handle_call({:del_port, options}, _from, state) do + %State{client_pid: pid, ovs_uuid: ovs} = state + eq_br_name = [{"name", "==", options[:bridge]}] + eq_ovs_uuid = [{"_uuid", "==", ovs}] + next_config = [{"next_cfg", "+=", 1}] + + bridge = %{ports: options[:ports]} + + replies = + xact([ + :eovsdb_op.update(@bridge, eq_br_name, bridge), + :eovsdb_op.mutate(@open_vswitch, eq_ovs_uuid, next_config) + ], pid) + + {:reply, replies, state} + end + + def handle_call({:set_controller, options}, _from, state) do + %State{client_pid: pid, ovs_uuid: ovs} = state + controller = %{ + target: options[:target], + connection_mode: options[:connection_mode], + controller_rate_limit: options[:controller_rate_limit], + controller_burst_limit: options[:controller_burst_limit], + } + + bridge = %{ + protocols: options[:protocol], + controller: ["named-uuid", "controller"], + } + + replies = + xact([ + :eovsdb_op.insert(@controller, controller, "controller"), + :eovsdb_op.update(@bridge, [{"name", "==", options[:bridge]}], bridge), + :eovsdb_op.mutate(@open_vswitch, [{"_uuid", "==", ovs}], [{"next_cfg", "+=", 1}]) + ], pid) + + {:reply, replies, state} + end + + def handle_call({:add_port, options}, _from, state) do + %State{client_pid: pid, ovs_uuid: ovs} = state + + port = %{ + name: options[:name], + interfaces: ["named-uuid", "interface"], + } + + iface_options = [ + ["remote_ip", options[:remote_ip]], + ["local_ip", options[:local_ip]], + ["in_key", options[:in_key]], + ["out_key", options[:out_key]], + ["key", options[:key]], + ["tos", options[:tos]], + ["ttl", options[:ttl]], + ["df_default", options[:df_default]], + ["csum", options[:csum]], + ["peer_cert", options[:peer_cert]], + ["certificate", options[:certificate]], + ["private_key", options[:private_key]], + ["psk", options[:psk]] + ] + + interface = %{ + name: options[:name], + type: options[:type], + ingress_policing_rate: options[:ingress_policing_rate], + ingress_policing_burst: options[:ingress_policing_burst], + ofport_request: make_ovsdb_set(options[:ofport_request]), + options: make_ovsdb_map(iface_options) + } + + bridge = %{ports: add_elem_to_set(options[:ports], ["named-uuid", "port"])} + + next_config = [{"next_cfg", "+=", 1}] + eq_br_name = [{"name", "==", options[:bridge]}] + eq_ovs_uuid = [{"_uuid", "==", ovs}] + + replies = + xact( + [ + :eovsdb_op.insert(@interface, interface, "interface"), + :eovsdb_op.insert(@port, port, "port"), + :eovsdb_op.update(@bridge, eq_br_name, bridge), + :eovsdb_op.mutate(@open_vswitch, eq_ovs_uuid, next_config) + ], + pid + ) + + {:reply, replies, state} + end + def handle_call({:find_by_name, table, name}, _from, state) do %State{client_pid: pid} = state - query = :eovsdb_op.select('*', table, [{"name", "==", name}]) - - reply = - case xact(query, pid) do - [%{"rows" => []}] -> :not_found - [%{"rows" => [row]}] -> row - end - + reply = do_find_by_name(pid, table, name) {:reply, reply, state} end @@ -171,4 +374,29 @@ defmodule OVSDB.OpenvSwitch do defp xact(query, pid) when is_map(query) do xact([query], pid) end + + defp do_find_by_name(pid, table, name) do + query = :eovsdb_op.select('*', table, [{"name", "==", name}]) + case xact(query, pid) do + [%{"rows" => []}] -> :not_found + [%{"rows" => [row]}] -> row + end + end + + defp make_ovsdb_map(map), do: make_ovsdb_map([], map) + + defp make_ovsdb_map(acc, []), do: ["map", Enum.reverse(acc)] + defp make_ovsdb_map(acc, [[_key, nil]|rest]), do: make_ovsdb_map(acc, rest) + defp make_ovsdb_map(acc, [attr|rest]), do: make_ovsdb_map([attr|acc], rest) + + defp make_ovsdb_set(nil), do: ["set", []] + defp make_ovsdb_set([_|_] = list), do: ["set", list] + defp make_ovsdb_set(value), do: value + + defp add_elem_to_set(["set", []], value), do: value + defp add_elem_to_set(["set", values], value), do: ["set", values ++ value] + defp add_elem_to_set(["uuid", _] = uuid, value), do: ["set", [uuid] ++ [value]] + + defp del_elem_from_set(["set", values], value), do: ["set", values -- [value]] + defp del_elem_from_set(["uuid", _], _value), do: ["set", []] end