From 84a827eea14e5b52ba2b29b49cd5518b84b3b63d Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Mon, 22 Apr 2019 19:17:30 +0900 Subject: [PATCH] quality: Add test cases for openflow actions --- lib/openflow/actions/nx_controller2.ex | 26 +++- lib/openflow/actions/nx_nat.ex | 196 ++++++++++++++----------- mix.exs | 10 +- mix.lock | 10 ++ test/ofp_action_test.exs | 34 ++++- 5 files changed, 188 insertions(+), 88 deletions(-) diff --git a/lib/openflow/actions/nx_controller2.ex b/lib/openflow/actions/nx_controller2.ex index ef60a1e..c9d5620 100644 --- a/lib/openflow/actions/nx_controller2.ex +++ b/lib/openflow/actions/nx_controller2.ex @@ -25,7 +25,31 @@ defmodule Openflow.Action.NxController2 do alias __MODULE__ 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{ max_len: options[:max_len] || :no_buffer, id: options[:id] || 0, diff --git a/lib/openflow/actions/nx_nat.ex b/lib/openflow/actions/nx_nat.ex index 0c468e4..f6694ee 100644 --- a/lib/openflow/actions/nx_nat.ex +++ b/lib/openflow/actions/nx_nat.ex @@ -15,35 +15,46 @@ defmodule Openflow.Action.NxNat do alias __MODULE__ alias Openflow.Action.Experimenter - def new(options \\ []) do - flags = Keyword.get(options, :flags, []) - ipv4_min = Keyword.get(options, :ipv4_min) - ipv4_max = Keyword.get(options, :ipv4_max) - ipv6_min = Keyword.get(options, :ipv6_min) - ipv6_max = Keyword.get(options, :ipv6_max) - proto_min = Keyword.get(options, :proto_min) - proto_max = Keyword.get(options, :proto_max) + @type in_addr :: :inet.ip4_address() + @type in6_addr :: :inet.ip6_address() + @type port_number :: :inet.port_number() + @type flag :: :src | :dst | :persistent | :protocol_hash | :protocol_random + @type nat_range :: :ipv4_min | :ipv4_max | :ipv6_min | :ipv6_max | :proto_min | :proto_max + @type t :: %NxNat{ + 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() + } + @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{ - flags: flags, - ipv4_min: ipv4_min, - ipv4_max: ipv4_max, - ipv6_min: ipv6_min, - ipv6_max: ipv6_max, - proto_min: proto_min, - proto_max: proto_max + flags: options[:flags] || [], + ipv4_min: options[:ipv4_min], + ipv4_max: options[:ipv4_max], + ipv6_min: options[:ipv6_min], + ipv6_max: options[:ipv6_max], + proto_min: options[:proto_min], + proto_max: options[:proto_max] } end + @spec to_binary(t()) :: binary() def to_binary(%NxNat{flags: flags} = nat) do flags_int = Openflow.Enums.flags_to_int(flags, :nx_nat_flags) - - range_flags = - nat - |> get_ranges - |> Openflow.Enums.flags_to_int(:nx_nat_range) - |> Openflow.Enums.int_to_flags(:nx_nat_range) - + range_flags = get_range_flags(nat) ranges_bin = encode_ranges("", range_flags, nat) range_flags_int = Openflow.Enums.flags_to_int(range_flags, :nx_nat_range) @@ -57,6 +68,7 @@ defmodule Openflow.Action.NxNat do >>) end + @spec read(binary()) :: t() 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 flags = Openflow.Enums.int_to_flags(flags_int, :nx_nat_flags) @@ -66,81 +78,95 @@ defmodule Openflow.Action.NxNat do # private functions - defp get_ranges(nat) do - nat - |> Map.from_struct() - |> Map.delete(:flags) - |> Enum.map(fn {k, v} -> if(not is_nil(v), do: k, else: nil) end) - |> Enum.filter(fn v -> not is_nil(v) end) + @range_keys [:ipv4_min, :ipv4_max, :ipv6_min, :ipv6_max, :proto_min, :proto_max] + + @spec get_range_flags(t()) :: [nat_range()] + defp get_range_flags(nat) do + @range_keys + |> 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 + @spec encode_ranges(binary(), [nat_range()], t()) :: binary() defp encode_ranges(acc, [], _nat), do: acc - defp encode_ranges(acc, [range | rest], nat) do - cond do - range == :ipv4_min or range == :ipv4_max -> - case Map.get(nat, range) do - {a1, a2, a3, a4} -> - encode_ranges(<>, rest, nat) - - "" -> - encode_ranges(<>, rest, nat) - end - - range == :ipv6_min or range == :ipv6_max -> - case Map.get(nat, range) do - {a1, a2, a3, a4, a5, a6, a7, a8} -> - encode_ranges( - <>, - rest, - nat - ) - - "" -> - encode_ranges(<>, rest, nat) - end - - range == :proto_min or range == :proto_max -> - case Map.get(nat, range) do - proto when is_integer(proto) and proto in 1..0xFFFF -> - encode_ranges(<>, rest, nat) - - _ -> - encode_ranges(<>, rest, nat) - end - end + defp encode_ranges(acc, [:ipv4_min | rest], %NxNat{ipv4_min: {a1, a2, a3, a4}} = nat) do + encode_ranges(<>, rest, nat) end + defp encode_ranges(acc, [:ipv4_max | rest], %NxNat{ipv4_max: {a1, a2, a3, a4}} = nat) do + encode_ranges(<>, rest, nat) + end + + defp encode_ranges( + acc, + [:ipv6_min | rest], + %NxNat{ipv6_min: {a1, a2, a3, a4, a5, a6, a7, a8}} = nat + ) do + encode_ranges( + <>, + rest, + nat + ) + end + + defp encode_ranges( + acc, + [:ipv6_max | rest], + %NxNat{ipv6_max: {a1, a2, a3, a4, a5, a6, a7, a8}} = nat + ) do + encode_ranges( + <>, + rest, + nat + ) + end + + defp encode_ranges(acc, [:proto_min | rest], %NxNat{proto_min: proto} = nat) + when is_integer(proto) do + encode_ranges(<>, rest, nat) + end + + defp encode_ranges(acc, [:proto_max | rest], %NxNat{proto_max: proto} = nat) + when is_integer(proto) do + encode_ranges(<>, rest, nat) + end + + @spec decode_ranges(t(), [nat_range()], binary()) :: t() defp decode_ranges(nat, [], _), do: nat - defp decode_ranges(nat, [range | ranges], bin) do - cond do - range == :ipv4_min or range == :ipv4_max -> - case bin do - <> -> - decode_ranges(struct(nat, %{range => {a1, a2, a3, a4}}), ranges, rest) + defp decode_ranges(acc, [:ipv4_min | rest], <>) do + decode_ranges(%{acc | ipv4_min: {a1, a2, a3, a4}}, rest, binary) + end - rest -> - decode_ranges(struct(nat, %{range => ""}), ranges, rest) - end + defp decode_ranges(acc, [:ipv4_max | rest], <>) do + decode_ranges(%{acc | ipv4_max: {a1, a2, a3, a4}}, rest, binary) + end - range == :ipv6_min or range == :ipv6_max -> - case bin do - <> -> - decode_ranges(struct(nat, %{range => {a1, a2, a3, a4, a5, a6, a7, a8}}), ranges, rest) + defp decode_ranges( + acc, + [:ipv6_min | rest], + <> + ) do + decode_ranges(%{acc | ipv6_min: {a1, a2, a3, a4, a5, a6, a7, a8}}, rest, binary) + end - rest -> - decode_ranges(struct(nat, %{range => ""}), ranges, rest) - end + defp decode_ranges( + acc, + [:ipv6_max | rest], + <> + ) do + decode_ranges(%{acc | ipv6_max: {a1, a2, a3, a4, a5, a6, a7, a8}}, rest, binary) + end - range == :proto_min or range == :proto_max -> - case bin do - <> when proto in 1..0xFFFF -> - decode_ranges(struct(nat, %{range => proto}), ranges, rest) + defp decode_ranges(acc, [:proto_min | rest], <>) do + decode_ranges(%{acc | proto_min: proto}, rest, binary) + end - rest -> - decode_ranges(struct(nat, %{range => ""}), ranges, rest) - end - end + defp decode_ranges(acc, [:proto_max | rest], <>) do + decode_ranges(%{acc | proto_max: proto}, rest, binary) end end diff --git a/mix.exs b/mix.exs index c7d4380..22a9bff 100644 --- a/mix.exs +++ b/mix.exs @@ -11,7 +11,14 @@ defmodule Tres.Mixfile do compilers: [:erlang] ++ Mix.compilers(), deps: deps(), 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 @@ -27,6 +34,7 @@ defmodule Tres.Mixfile do {:eovsdb, github: "shun159/eovsdb", branch: "master"}, {:jsone, github: "sile/jsone", tag: "1.4.6", override: true}, {:epcap, github: "msantos/epcap", branch: "master", only: :test}, + {:excoveralls, "~> 0.10", only: :test}, # Document {:earmark, "~> 1.2.6", only: :dev, runtime: false}, {:ex_doc, "~> 0.19", only: :dev, runtime: false} diff --git a/mix.lock b/mix.lock index d7fe73c..43875ca 100644 --- a/mix.lock +++ b/mix.lock @@ -1,17 +1,27 @@ %{ "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"}, "eovsdb": {:git, "https://github.com/shun159/eovsdb.git", "1ff1572708d72fd25631c681f2102407903252a3", [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"}, + "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"}, "gen_stage": {:hex, :gen_stage, "0.12.2", "e0e347cbb1ceb5f4e68a526aec4d64b54ad721f0a8b30aa9d28e0ad749419cbb", [:mix], [], "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"]}, "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"}, + "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"}, + "parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm"}, "pkt": {:git, "https://github.com/msantos/pkt.git", "ff0e9a7d28cdae941bce935602cd252cad1ea296", [branch: "master"]}, "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"]}, } diff --git a/test/ofp_action_test.exs b/test/ofp_action_test.exs index 8d6cd71..1387363 100644 --- a/test/ofp_action_test.exs +++ b/test/ofp_action_test.exs @@ -255,7 +255,8 @@ defmodule OfpActionTest do id: 5678, reason: :invalid_ttl, userdata: <<1, 2, 3, 4, 5>>, - pause: true + pause: true, + meter_id: 0 ) assert actions == [controller2] @@ -267,6 +268,37 @@ defmodule OfpActionTest do |> Kernel.==(controller2) |> assert() 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 describe "Openflow.Action.NxConntrack" do