quality: Add test cases for openflow actions

This commit is contained in:
Eishun Kondoh 2019-04-22 19:17:30 +09:00
parent a5eddffb8c
commit 539d77df16
5 changed files with 188 additions and 88 deletions

View file

@ -25,7 +25,31 @@ defmodule Openflow.Action.NxController2 do
alias __MODULE__ alias __MODULE__
alias Openflow.Action.Experimenter alias Openflow.Action.Experimenter
def new(options \\ []) do @type max_len :: pos_integer() | :max | :no_buffer
@type packet_in_reason ::
:no_match | :action | :invalid_ttl | :action_set | :group | :packet_out
@type meter_id :: pos_integer() | :max | :slowpath | :controller | :all
@type t :: %NxController2{
max_len: max_len(),
id: non_neg_integer(),
reason: packet_in_reason(),
userdata: binary(),
pause: boolean(),
meter_id: meter_id()
}
@spec new(
max_len: max_len(),
id: non_neg_integer(),
reason: packet_in_reason(),
userdata: binary(),
pause: boolean(),
meter_id: meter_id()
) :: t()
def new(options \\ [])
def new(options) when is_list(options) do
%NxController2{ %NxController2{
max_len: options[:max_len] || :no_buffer, max_len: options[:max_len] || :no_buffer,
id: options[:id] || 0, id: options[:id] || 0,

View file

@ -15,35 +15,46 @@ defmodule Openflow.Action.NxNat do
alias __MODULE__ alias __MODULE__
alias Openflow.Action.Experimenter alias Openflow.Action.Experimenter
def new(options \\ []) do @type in_addr :: :inet.ip4_address()
flags = Keyword.get(options, :flags, []) @type in6_addr :: :inet.ip6_address()
ipv4_min = Keyword.get(options, :ipv4_min) @type port_number :: :inet.port_number()
ipv4_max = Keyword.get(options, :ipv4_max) @type flag :: :src | :dst | :persistent | :protocol_hash | :protocol_random
ipv6_min = Keyword.get(options, :ipv6_min) @type nat_range :: :ipv4_min | :ipv4_max | :ipv6_min | :ipv6_max | :proto_min | :proto_max
ipv6_max = Keyword.get(options, :ipv6_max) @type t :: %NxNat{
proto_min = Keyword.get(options, :proto_min) flags: [flag()],
proto_max = Keyword.get(options, :proto_max) ipv4_min: in_addr(),
ipv4_max: in_addr(),
ipv6_min: in6_addr(),
ipv6_max: in6_addr(),
proto_min: port_number(),
proto_max: port_number()
}
@spec new(
flags: [flag()],
ipv4_min: in_addr(),
ipv4_max: in_addr(),
ipv6_min: in6_addr(),
ipv6_max: in6_addr(),
proto_min: port_number(),
proto_max: port_number()
) :: t()
def new(options \\ []) do
%NxNat{ %NxNat{
flags: flags, flags: options[:flags] || [],
ipv4_min: ipv4_min, ipv4_min: options[:ipv4_min],
ipv4_max: ipv4_max, ipv4_max: options[:ipv4_max],
ipv6_min: ipv6_min, ipv6_min: options[:ipv6_min],
ipv6_max: ipv6_max, ipv6_max: options[:ipv6_max],
proto_min: proto_min, proto_min: options[:proto_min],
proto_max: proto_max proto_max: options[:proto_max]
} }
end end
@spec to_binary(t()) :: binary()
def to_binary(%NxNat{flags: flags} = nat) do def to_binary(%NxNat{flags: flags} = nat) do
flags_int = Openflow.Enums.flags_to_int(flags, :nx_nat_flags) flags_int = Openflow.Enums.flags_to_int(flags, :nx_nat_flags)
range_flags = get_range_flags(nat)
range_flags =
nat
|> get_ranges
|> Openflow.Enums.flags_to_int(:nx_nat_range)
|> Openflow.Enums.int_to_flags(:nx_nat_range)
ranges_bin = encode_ranges("", range_flags, nat) ranges_bin = encode_ranges("", range_flags, nat)
range_flags_int = Openflow.Enums.flags_to_int(range_flags, :nx_nat_range) range_flags_int = Openflow.Enums.flags_to_int(range_flags, :nx_nat_range)
@ -57,6 +68,7 @@ defmodule Openflow.Action.NxNat do
>>) >>)
end end
@spec read(binary()) :: t()
def read(<<@experimenter::32, @nxast::16, body::bytes>>) do def read(<<@experimenter::32, @nxast::16, body::bytes>>) do
<<0::size(2)-unit(8), flags_int::16, range_flags_int::16, ranges_bin::bytes>> = body <<0::size(2)-unit(8), flags_int::16, range_flags_int::16, ranges_bin::bytes>> = body
flags = Openflow.Enums.int_to_flags(flags_int, :nx_nat_flags) flags = Openflow.Enums.int_to_flags(flags_int, :nx_nat_flags)
@ -66,81 +78,95 @@ defmodule Openflow.Action.NxNat do
# private functions # private functions
defp get_ranges(nat) do @range_keys [:ipv4_min, :ipv4_max, :ipv6_min, :ipv6_max, :proto_min, :proto_max]
nat
|> Map.from_struct() @spec get_range_flags(t()) :: [nat_range()]
|> Map.delete(:flags) defp get_range_flags(nat) do
|> Enum.map(fn {k, v} -> if(not is_nil(v), do: k, else: nil) end) @range_keys
|> Enum.filter(fn v -> not is_nil(v) end) |> Enum.reduce([], fn
key, acc when key in @range_keys ->
if not is_nil(Map.get(nat, key)), do: [key | acc], else: acc
end)
|> Enum.reverse()
end end
@spec encode_ranges(binary(), [nat_range()], t()) :: binary()
defp encode_ranges(acc, [], _nat), do: acc defp encode_ranges(acc, [], _nat), do: acc
defp encode_ranges(acc, [range | rest], nat) do defp encode_ranges(acc, [:ipv4_min | rest], %NxNat{ipv4_min: {a1, a2, a3, a4}} = nat) do
cond do
range == :ipv4_min or range == :ipv4_max ->
case Map.get(nat, range) do
{a1, a2, a3, a4} ->
encode_ranges(<<acc::bytes, a1, a2, a3, a4>>, rest, nat) encode_ranges(<<acc::bytes, a1, a2, a3, a4>>, rest, nat)
"" ->
encode_ranges(<<acc::bytes>>, rest, nat)
end end
range == :ipv6_min or range == :ipv6_max -> defp encode_ranges(acc, [:ipv4_max | rest], %NxNat{ipv4_max: {a1, a2, a3, a4}} = nat) do
case Map.get(nat, range) do encode_ranges(<<acc::bytes, a1, a2, a3, a4>>, rest, nat)
{a1, a2, a3, a4, a5, a6, a7, a8} -> end
defp encode_ranges(
acc,
[:ipv6_min | rest],
%NxNat{ipv6_min: {a1, a2, a3, a4, a5, a6, a7, a8}} = nat
) do
encode_ranges( encode_ranges(
<<acc::bytes, a1::16, a2::16, a3::16, a4::16, a5::16, a6::16, a7::16, a8::16>>, <<acc::bytes, a1::16, a2::16, a3::16, a4::16, a5::16, a6::16, a7::16, a8::16>>,
rest, rest,
nat nat
) )
"" ->
encode_ranges(<<acc::bytes>>, rest, nat)
end end
range == :proto_min or range == :proto_max -> defp encode_ranges(
case Map.get(nat, range) do acc,
proto when is_integer(proto) and proto in 1..0xFFFF -> [:ipv6_max | rest],
%NxNat{ipv6_max: {a1, a2, a3, a4, a5, a6, a7, a8}} = nat
) do
encode_ranges(
<<acc::bytes, a1::16, a2::16, a3::16, a4::16, a5::16, a6::16, a7::16, a8::16>>,
rest,
nat
)
end
defp encode_ranges(acc, [:proto_min | rest], %NxNat{proto_min: proto} = nat)
when is_integer(proto) do
encode_ranges(<<acc::bytes, proto::16>>, rest, nat) encode_ranges(<<acc::bytes, proto::16>>, rest, nat)
_ ->
encode_ranges(<<acc::bytes>>, rest, nat)
end
end
end end
defp encode_ranges(acc, [:proto_max | rest], %NxNat{proto_max: proto} = nat)
when is_integer(proto) do
encode_ranges(<<acc::bytes, proto::16>>, rest, nat)
end
@spec decode_ranges(t(), [nat_range()], binary()) :: t()
defp decode_ranges(nat, [], _), do: nat defp decode_ranges(nat, [], _), do: nat
defp decode_ranges(nat, [range | ranges], bin) do defp decode_ranges(acc, [:ipv4_min | rest], <<a1, a2, a3, a4, binary::bytes>>) do
cond do decode_ranges(%{acc | ipv4_min: {a1, a2, a3, a4}}, rest, binary)
range == :ipv4_min or range == :ipv4_max ->
case bin do
<<a1, a2, a3, a4, rest::bytes>> ->
decode_ranges(struct(nat, %{range => {a1, a2, a3, a4}}), ranges, rest)
rest ->
decode_ranges(struct(nat, %{range => ""}), ranges, rest)
end end
range == :ipv6_min or range == :ipv6_max -> defp decode_ranges(acc, [:ipv4_max | rest], <<a1, a2, a3, a4, binary::bytes>>) do
case bin do decode_ranges(%{acc | ipv4_max: {a1, a2, a3, a4}}, rest, binary)
<<a1::16, a2::16, a3::16, a4::16, a5::16, a6::16, a7::16, a8::16, rest::bytes>> ->
decode_ranges(struct(nat, %{range => {a1, a2, a3, a4, a5, a6, a7, a8}}), ranges, rest)
rest ->
decode_ranges(struct(nat, %{range => ""}), ranges, rest)
end end
range == :proto_min or range == :proto_max -> defp decode_ranges(
case bin do acc,
<<proto::16, rest::bytes>> when proto in 1..0xFFFF -> [:ipv6_min | rest],
decode_ranges(struct(nat, %{range => proto}), ranges, rest) <<a1::16, a2::16, a3::16, a4::16, a5::16, a6::16, a7::16, a8::16, binary::bytes>>
) do
decode_ranges(%{acc | ipv6_min: {a1, a2, a3, a4, a5, a6, a7, a8}}, rest, binary)
end
rest -> defp decode_ranges(
decode_ranges(struct(nat, %{range => ""}), ranges, rest) acc,
end [:ipv6_max | rest],
end <<a1::16, a2::16, a3::16, a4::16, a5::16, a6::16, a7::16, a8::16, binary::bytes>>
) do
decode_ranges(%{acc | ipv6_max: {a1, a2, a3, a4, a5, a6, a7, a8}}, rest, binary)
end
defp decode_ranges(acc, [:proto_min | rest], <<proto::16, binary::bytes>>) do
decode_ranges(%{acc | proto_min: proto}, rest, binary)
end
defp decode_ranges(acc, [:proto_max | rest], <<proto::16, binary::bytes>>) do
decode_ranges(%{acc | proto_max: proto}, rest, binary)
end end
end end

10
mix.exs
View file

@ -11,7 +11,14 @@ defmodule Tres.Mixfile do
compilers: [:erlang] ++ Mix.compilers(), compilers: [:erlang] ++ Mix.compilers(),
deps: deps(), deps: deps(),
aliases: [test: "test --no-start", compile: ["escript.build"]], aliases: [test: "test --no-start", compile: ["escript.build"]],
docs: docs() docs: docs(),
# test_coverage: [tool: ExCoveralls],
preferred_cli_env: [
coveralls: :test,
"coveralls.detail": :test,
"coveralls.post": :test,
"coveralls.html": :test
]
] ]
end end
@ -27,6 +34,7 @@ defmodule Tres.Mixfile do
{:eovsdb, github: "shun159/eovsdb", branch: "master"}, {:eovsdb, github: "shun159/eovsdb", branch: "master"},
{:jsone, github: "sile/jsone", tag: "1.4.6", override: true}, {:jsone, github: "sile/jsone", tag: "1.4.6", override: true},
{:epcap, github: "msantos/epcap", branch: "master", only: :test}, {:epcap, github: "msantos/epcap", branch: "master", only: :test},
{:excoveralls, "~> 0.10", only: :test},
# Document # Document
{:earmark, "~> 1.2.6", only: :dev, runtime: false}, {:earmark, "~> 1.2.6", only: :dev, runtime: false},
{:ex_doc, "~> 0.19", only: :dev, runtime: false} {:ex_doc, "~> 0.19", only: :dev, runtime: false}

View file

@ -1,17 +1,27 @@
%{ %{
"binpp": {:git, "https://github.com/jtendo/binpp.git", "64bd68d215d1a6cd35871e7c134d7fe2e46214ea", [branch: "master"]}, "binpp": {:git, "https://github.com/jtendo/binpp.git", "64bd68d215d1a6cd35871e7c134d7fe2e46214ea", [branch: "master"]},
"certifi": {:hex, :certifi, "2.5.1", "867ce347f7c7d78563450a18a6a28a8090331e77fa02380b4a21962a65d36ee5", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"},
"earmark": {:hex, :earmark, "1.2.6", "b6da42b3831458d3ecc57314dff3051b080b9b2be88c2e5aa41cd642a5b044ed", [:mix], [], "hexpm"}, "earmark": {:hex, :earmark, "1.2.6", "b6da42b3831458d3ecc57314dff3051b080b9b2be88c2e5aa41cd642a5b044ed", [:mix], [], "hexpm"},
"eovsdb": {:git, "https://github.com/shun159/eovsdb.git", "1ff1572708d72fd25631c681f2102407903252a3", [branch: "master"]}, "eovsdb": {:git, "https://github.com/shun159/eovsdb.git", "1ff1572708d72fd25631c681f2102407903252a3", [branch: "master"]},
"epcap": {:git, "https://github.com/msantos/epcap.git", "9566f0420a4dcf1292c1a1afd9339c35dbdfd041", [branch: "master"]}, "epcap": {:git, "https://github.com/msantos/epcap.git", "9566f0420a4dcf1292c1a1afd9339c35dbdfd041", [branch: "master"]},
"ex_doc": {:hex, :ex_doc, "0.19.3", "3c7b0f02851f5fc13b040e8e925051452e41248f685e40250d7e40b07b9f8c10", [:mix], [{:earmark, "~> 1.2", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.10", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"}, "ex_doc": {:hex, :ex_doc, "0.19.3", "3c7b0f02851f5fc13b040e8e925051452e41248f685e40250d7e40b07b9f8c10", [:mix], [{:earmark, "~> 1.2", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.10", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"},
"excoveralls": {:hex, :excoveralls, "0.10.6", "e2b9718c9d8e3ef90bc22278c3f76c850a9f9116faf4ebe9678063310742edc2", [:mix], [{:hackney, "~> 1.13", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"},
"flow": {:hex, :flow, "0.12.0", "32c5a5f3ff6693e004b6c17a8c64dce2f8cdaf9564912d79427176013a586ab6", [], [{:gen_stage, "~> 0.12.0", [hex: :gen_stage, repo: "hexpm", optional: false]}], "hexpm"}, "flow": {:hex, :flow, "0.12.0", "32c5a5f3ff6693e004b6c17a8c64dce2f8cdaf9564912d79427176013a586ab6", [], [{:gen_stage, "~> 0.12.0", [hex: :gen_stage, repo: "hexpm", optional: false]}], "hexpm"},
"gen_stage": {:hex, :gen_stage, "0.12.2", "e0e347cbb1ceb5f4e68a526aec4d64b54ad721f0a8b30aa9d28e0ad749419cbb", [:mix], [], "hexpm"}, "gen_stage": {:hex, :gen_stage, "0.12.2", "e0e347cbb1ceb5f4e68a526aec4d64b54ad721f0a8b30aa9d28e0ad749419cbb", [:mix], [], "hexpm"},
"gen_state_machine": {:hex, :gen_state_machine, "2.0.1", "85efd5a0376929c3a4246dd943e17564a2908c7ddd7acd242d84594e785d83f8", [], [], "hexpm"}, "gen_state_machine": {:hex, :gen_state_machine, "2.0.1", "85efd5a0376929c3a4246dd943e17564a2908c7ddd7acd242d84594e785d83f8", [], [], "hexpm"},
"hackney": {:hex, :hackney, "1.15.1", "9f8f471c844b8ce395f7b6d8398139e26ddca9ebc171a8b91342ee15a19963f4", [:rebar3], [{:certifi, "2.5.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.4", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"},
"idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"},
"jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"},
"jsone": {:git, "https://github.com/sile/jsone.git", "b23d312a5ed051ea7ad0989a9f2cb1a9c3f9a502", [tag: "1.4.6"]}, "jsone": {:git, "https://github.com/sile/jsone.git", "b23d312a5ed051ea7ad0989a9f2cb1a9c3f9a502", [tag: "1.4.6"]},
"makeup": {:hex, :makeup, "0.8.0", "9cf32aea71c7fe0a4b2e9246c2c4978f9070257e5c9ce6d4a28ec450a839b55f", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"}, "makeup": {:hex, :makeup, "0.8.0", "9cf32aea71c7fe0a4b2e9246c2c4978f9070257e5c9ce6d4a28ec450a839b55f", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"},
"makeup_elixir": {:hex, :makeup_elixir, "0.13.0", "be7a477997dcac2e48a9d695ec730b2d22418292675c75aa2d34ba0909dcdeda", [:mix], [{:makeup, "~> 0.8", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"}, "makeup_elixir": {:hex, :makeup_elixir, "0.13.0", "be7a477997dcac2e48a9d695ec730b2d22418292675c75aa2d34ba0909dcdeda", [:mix], [{:makeup, "~> 0.8", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"},
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"},
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm"},
"nimble_parsec": {:hex, :nimble_parsec, "0.5.0", "90e2eca3d0266e5c53f8fbe0079694740b9c91b6747f2b7e3c5d21966bba8300", [:mix], [], "hexpm"}, "nimble_parsec": {:hex, :nimble_parsec, "0.5.0", "90e2eca3d0266e5c53f8fbe0079694740b9c91b6747f2b7e3c5d21966bba8300", [:mix], [], "hexpm"},
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm"},
"pkt": {:git, "https://github.com/msantos/pkt.git", "ff0e9a7d28cdae941bce935602cd252cad1ea296", [branch: "master"]}, "pkt": {:git, "https://github.com/msantos/pkt.git", "ff0e9a7d28cdae941bce935602cd252cad1ea296", [branch: "master"]},
"ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm"}, "ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm"},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], [], "hexpm"},
"unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm"},
"uuid": {:git, "https://github.com/avtobiff/erlang-uuid.git", "585c2474afb4a597ae8c8bf6d21e5a9c73f18e0b", [tag: "v0.5.0"]}, "uuid": {:git, "https://github.com/avtobiff/erlang-uuid.git", "585c2474afb4a597ae8c8bf6d21e5a9c73f18e0b", [tag: "v0.5.0"]},
} }

View file

@ -255,7 +255,8 @@ defmodule OfpActionTest do
id: 5678, id: 5678,
reason: :invalid_ttl, reason: :invalid_ttl,
userdata: <<1, 2, 3, 4, 5>>, userdata: <<1, 2, 3, 4, 5>>,
pause: true pause: true,
meter_id: 0
) )
assert actions == [controller2] assert actions == [controller2]
@ -267,6 +268,37 @@ defmodule OfpActionTest do
|> Kernel.==(controller2) |> Kernel.==(controller2)
|> assert() |> assert()
end end
test "with meter_id" do
controller2 =
[meter_id: 0xFACE]
|> Openflow.Action.NxController2.new()
|> Openflow.Action.NxController2.to_binary()
|> Openflow.Action.read()
|> Enum.at(0)
assert controller2.max_len == :no_buffer
assert controller2.id == 0
assert controller2.reason == :action
assert controller2.userdata == ""
assert controller2.pause == false
assert controller2.meter_id == 0xFACE
end
test "with no options" do
controller2 =
Openflow.Action.NxController2.new()
|> Openflow.Action.NxController2.to_binary()
|> Openflow.Action.read()
|> Enum.at(0)
assert controller2.max_len == :no_buffer
assert controller2.id == 0
assert controller2.reason == :action
assert controller2.userdata == ""
assert controller2.pause == false
assert controller2.meter_id == 0
end
end end
describe "Openflow.Action.NxConntrack" do describe "Openflow.Action.NxConntrack" do