quality/tres: Add handler testcases
This commit is contained in:
parent
0a6d7f31d6
commit
4965bc60ec
9 changed files with 273 additions and 98 deletions
|
|
@ -21,7 +21,9 @@ before_install:
|
||||||
- sudo ovs-vsctl add-br br0
|
- sudo ovs-vsctl add-br br0
|
||||||
- sudo ovs-vsctl set bridge br0 datapath_type=netdev
|
- sudo ovs-vsctl set bridge br0 datapath_type=netdev
|
||||||
- sudo ovs-vsctl set bridge br0 protocols=OpenFlow13
|
- sudo ovs-vsctl set bridge br0 protocols=OpenFlow13
|
||||||
|
- sudo ovs-vsctl set bridge br0 other-config:datapath-id=0000000000000001
|
||||||
- sudo ovs-vsctl set-controller br0 tcp:127.0.0.1:6653
|
- sudo ovs-vsctl set-controller br0 tcp:127.0.0.1:6653
|
||||||
|
- sudo ovs-vsctl set-manager ptcp:6640
|
||||||
- sudo ovs-vsctl show
|
- sudo ovs-vsctl show
|
||||||
- cd -
|
- cd -
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,29 +43,4 @@ defmodule Openflow.PacketIn do
|
||||||
data: data
|
data: data
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_binary(%PacketIn{} = packet_in) do
|
|
||||||
%PacketIn{
|
|
||||||
buffer_id: buffer_id,
|
|
||||||
total_len: total_len,
|
|
||||||
reason: reason,
|
|
||||||
table_id: table_id,
|
|
||||||
cookie: cookie,
|
|
||||||
in_port: in_port,
|
|
||||||
match: match_fields,
|
|
||||||
data: data
|
|
||||||
} = packet_in
|
|
||||||
|
|
||||||
buffer_id_int = Openflow.Utils.get_enum(buffer_id, :buffer_id)
|
|
||||||
reason_int = Openflow.Utils.get_enum(reason, :packet_in_reason)
|
|
||||||
table_id_int = Openflow.Utils.get_enum(table_id, :table_id)
|
|
||||||
|
|
||||||
match_fields_bin =
|
|
||||||
[{:in_port, in_port} | match_fields]
|
|
||||||
|> Openflow.Match.new()
|
|
||||||
|> Openflow.Match.to_binary()
|
|
||||||
|
|
||||||
<<buffer_id_int::32, total_len::16, reason_int::8, table_id_int::8, cookie::64,
|
|
||||||
match_fields_bin::bytes, 0::size(2)-unit(8), data::bytes>>
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -14,19 +14,8 @@ defmodule Openflow.Role.Reply do
|
||||||
|
|
||||||
def ofp_type, do: 25
|
def ofp_type, do: 25
|
||||||
|
|
||||||
def new(options \\ []) do
|
|
||||||
role = Keyword.get(options, :role, :nochange)
|
|
||||||
generation_id = Keyword.get(options, :generation_id, 0)
|
|
||||||
%Reply{role: role, generation_id: generation_id}
|
|
||||||
end
|
|
||||||
|
|
||||||
def read(<<role_int::32, 0::size(4)-unit(8), generation_id::64>>) do
|
def read(<<role_int::32, 0::size(4)-unit(8), generation_id::64>>) do
|
||||||
role = Openflow.Enums.to_atom(role_int, :controller_role)
|
role = Openflow.Enums.to_atom(role_int, :controller_role)
|
||||||
%Reply{role: role, generation_id: generation_id}
|
%Reply{role: role, generation_id: generation_id}
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_binary(%Reply{role: role, generation_id: generation_id}) do
|
|
||||||
role_int = Openflow.Enums.to_int(role, :controller_role)
|
|
||||||
<<role_int::32, 0::size(4)-unit(8), generation_id::64>>
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -21,11 +21,6 @@ defmodule Openflow.Role.Request do
|
||||||
%Request{xid: xid, role: role, generation_id: generation_id}
|
%Request{xid: xid, role: role, generation_id: generation_id}
|
||||||
end
|
end
|
||||||
|
|
||||||
def read(<<role_int::32, 0::size(4)-unit(8), generation_id::64>>) do
|
|
||||||
role = Openflow.Enums.to_atom(role_int, :controller_role)
|
|
||||||
%Request{role: role, generation_id: generation_id}
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_binary(%Request{role: role, generation_id: generation_id}) do
|
def to_binary(%Request{role: role, generation_id: generation_id}) do
|
||||||
role_int = Openflow.Enums.to_int(role, :controller_role)
|
role_int = Openflow.Enums.to_int(role, :controller_role)
|
||||||
<<role_int::32, 0::size(4)-unit(8), generation_id::64>>
|
<<role_int::32, 0::size(4)-unit(8), generation_id::64>>
|
||||||
|
|
|
||||||
|
|
@ -7,33 +7,50 @@ defmodule Tres.ExampleHandler do
|
||||||
defmodule State do
|
defmodule State do
|
||||||
defstruct datapath_id: nil,
|
defstruct datapath_id: nil,
|
||||||
aux_id: nil,
|
aux_id: nil,
|
||||||
conn_ref: nil
|
queue: :queue.new(),
|
||||||
|
client: nil
|
||||||
|
end
|
||||||
|
|
||||||
|
# API functions
|
||||||
|
|
||||||
|
@spec send(String.t(), map()) :: :ok
|
||||||
|
def send(datapath_id, msg), do: send_message(msg, datapath_id)
|
||||||
|
|
||||||
|
@spec get(datapath_id :: String.t()) :: map() | nil
|
||||||
|
def get(datapath_id) do
|
||||||
|
datapath_id
|
||||||
|
|> lookup_handler_pid()
|
||||||
|
|> GenServer.call(:get)
|
||||||
end
|
end
|
||||||
|
|
||||||
def start_link(datapath, args) do
|
def start_link(datapath, args) do
|
||||||
GenServer.start_link(__MODULE__, [datapath, args])
|
GenServer.start_link(__MODULE__, [datapath, args])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# GenServer callbacks
|
||||||
|
|
||||||
def init([{datapath_id, aux_id}, _args]) do
|
def init([{datapath_id, aux_id}, _args]) do
|
||||||
info(
|
:ok = info("datapath connected: #{datapath_id}")
|
||||||
"Switch Ready: " <>
|
:ok = send_flow_mod_add(datapath_id, priority: 0)
|
||||||
"datapath_id: #{datapath_id} " <> "aux_id: #{aux_id} " <> "on #{inspect(self())}"
|
{:ok, %State{datapath_id: datapath_id, aux_id: aux_id}}
|
||||||
)
|
|
||||||
|
|
||||||
_ = send_desc_stats_request(datapath_id)
|
|
||||||
_ = send_port_desc_stats_request(datapath_id)
|
|
||||||
state = %State{datapath_id: datapath_id, aux_id: aux_id}
|
|
||||||
{:ok, state}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_info(%PortDesc.Reply{datapath_id: datapath_id} = desc, state) do
|
def handle_call(:get, from, %State{queue: {[], []}} = state) do
|
||||||
handle_port_desc_stats_reply(desc, datapath_id)
|
{:noreply, %{state | client: from}}
|
||||||
{:noreply, state}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_info(%Desc.Reply{datapath_id: datapath_id} = desc, state) do
|
def handle_call(:get, _from, %State{} = state) do
|
||||||
handle_desc_stats_reply(desc, datapath_id)
|
{{:value, msg}, new_queue} = :queue.out(state.queue)
|
||||||
{:noreply, state}
|
{:reply, msg, %{state | queue: new_queue}}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_info(%{datapath_id: _datapath_id} = msg, %State{client: nil} = state) do
|
||||||
|
{:noreply, %{state | queue: :queue.in(msg, state.queue)}}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_info(%{datapath_id: _datapath_id} = msg, state) do
|
||||||
|
GenServer.reply(state.client, msg)
|
||||||
|
{:noreply, %{state | client: nil}}
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_info({:switch_disconnected, _reason}, state) do
|
def handle_info({:switch_disconnected, _reason}, state) do
|
||||||
|
|
@ -41,47 +58,10 @@ defmodule Tres.ExampleHandler do
|
||||||
{:stop, :normal, state}
|
{:stop, :normal, state}
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_info({:switch_hang, _datapath_id}, state) do
|
|
||||||
:ok = warn("Switch possible hang: datapath_id: #{state.datapath_id}")
|
|
||||||
{:noreply, state}
|
|
||||||
end
|
|
||||||
|
|
||||||
# `Catch all` function is required.
|
|
||||||
def handle_info(info, state) do
|
def handle_info(info, state) do
|
||||||
:ok = warn("unhandled message #{inspect(info)}: #{state.datapath_id}")
|
:ok = warn("unhandled message #{inspect(info)}: #{state.datapath_id}")
|
||||||
{:noreply, state}
|
{:noreply, state}
|
||||||
end
|
end
|
||||||
|
|
||||||
# private functions
|
# private functions
|
||||||
|
|
||||||
defp send_desc_stats_request(datapath_id) do
|
|
||||||
Desc.Request.new()
|
|
||||||
|> send_message(datapath_id)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp send_port_desc_stats_request(datapath_id) do
|
|
||||||
PortDesc.Request.new()
|
|
||||||
|> send_message(datapath_id)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp handle_desc_stats_reply(desc, datapath_id) do
|
|
||||||
info(
|
|
||||||
"Switch Desc: " <>
|
|
||||||
"mfr = #{desc.mfr_desc} " <>
|
|
||||||
"hw = #{desc.hw_desc} " <> "sw = #{desc.sw_desc} " <> "for #{datapath_id}"
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp handle_port_desc_stats_reply(port_desc, datapath_id) do
|
|
||||||
for port <- port_desc.ports do
|
|
||||||
info(
|
|
||||||
"Switch has port: " <>
|
|
||||||
"number = #{port.number} " <>
|
|
||||||
"hw_addr = #{port.hw_addr} " <>
|
|
||||||
"name = #{port.name} " <>
|
|
||||||
"config = #{inspect(port.config)} " <>
|
|
||||||
"current_speed = #{port.current_speed} " <> "on #{datapath_id}"
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,14 @@ defmodule OfpPacketIn2Test do
|
||||||
|> Kernel.elem(1)
|
|> Kernel.elem(1)
|
||||||
|
|
||||||
assert pktin.continuation_action_set == nil
|
assert pktin.continuation_action_set == nil
|
||||||
assert pktin.continuation_actions == [%Openflow.Action.NxResubmitTable{in_port: :in_port, table_id: 5}]
|
|
||||||
assert pktin.continuation_bridge == <<8, 137, 105, 58, 5, 77, 183, 237, 163, 58, 25, 166, 212, 167, 209, 11>>
|
assert pktin.continuation_actions == [
|
||||||
|
%Openflow.Action.NxResubmitTable{in_port: :in_port, table_id: 5}
|
||||||
|
]
|
||||||
|
|
||||||
|
assert pktin.continuation_bridge ==
|
||||||
|
<<8, 137, 105, 58, 5, 77, 183, 237, 163, 58, 25, 166, 212, 167, 209, 11>>
|
||||||
|
|
||||||
assert pktin.continuation_conntracked == nil
|
assert pktin.continuation_conntracked == nil
|
||||||
assert pktin.continuation_cookie == nil
|
assert pktin.continuation_cookie == nil
|
||||||
assert pktin.continuation_mirrors == nil
|
assert pktin.continuation_mirrors == nil
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,14 @@ defmodule OfpResumeTest do
|
||||||
|> Kernel.elem(1)
|
|> Kernel.elem(1)
|
||||||
|
|
||||||
assert pktin.continuation_action_set == nil
|
assert pktin.continuation_action_set == nil
|
||||||
assert pktin.continuation_actions == [%Openflow.Action.NxResubmitTable{in_port: :in_port, table_id: 5}]
|
|
||||||
assert pktin.continuation_bridge == <<8, 137, 105, 58, 5, 77, 183, 237, 163, 58, 25, 166, 212, 167, 209, 11>>
|
assert pktin.continuation_actions == [
|
||||||
|
%Openflow.Action.NxResubmitTable{in_port: :in_port, table_id: 5}
|
||||||
|
]
|
||||||
|
|
||||||
|
assert pktin.continuation_bridge ==
|
||||||
|
<<8, 137, 105, 58, 5, 77, 183, 237, 163, 58, 25, 166, 212, 167, 209, 11>>
|
||||||
|
|
||||||
assert pktin.continuation_conntracked == nil
|
assert pktin.continuation_conntracked == nil
|
||||||
assert pktin.continuation_cookie == nil
|
assert pktin.continuation_cookie == nil
|
||||||
assert pktin.continuation_mirrors == nil
|
assert pktin.continuation_mirrors == nil
|
||||||
|
|
|
||||||
44
test/lib/ovsdb/openvswitch_test.exs
Normal file
44
test/lib/ovsdb/openvswitch_test.exs
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
defmodule OVSDB.OpenvSwitchTest do
|
||||||
|
use ExUnit.Case, async: false
|
||||||
|
|
||||||
|
setup_all do
|
||||||
|
{:ok, pid} = OVSDB.start_child("127.0.0.1:6640")
|
||||||
|
{:ok, pid: pid}
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "OVSDB.OpenvSwitch.find_by_name/3" do
|
||||||
|
test "with pid, table and name", context do
|
||||||
|
%{"datapath_id" => "0000000000000001"} = OVSDB.OpenvSwitch.find_by_name(context.pid, "Bridge", "br0")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "OVSDB.OpenvSwitch.add_br/2" do
|
||||||
|
test "with options", context do
|
||||||
|
OVSDB.OpenvSwitch.add_br(
|
||||||
|
context.pid,
|
||||||
|
name: "brx",
|
||||||
|
datapath_id: "0000000000000003"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "OVSDB.OpenvSwitch.set_controller/2" do
|
||||||
|
test "with options", context do
|
||||||
|
OVSDB.OpenvSwitch.add_br(
|
||||||
|
context.pid,
|
||||||
|
name: "brx",
|
||||||
|
target: "tcp:127.0.0.1:6653",
|
||||||
|
connection_mode: "out-of-band",
|
||||||
|
controller_rate_limit: 100,
|
||||||
|
controller_burst_limit: 25,
|
||||||
|
protocol: "OpenFlow13"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "OVSDB.OpenvSwitch.del_br/2" do
|
||||||
|
test "with pid and name", context do
|
||||||
|
OVSDB.OpenvSwitch.del_br(context.pid, "brx")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
178
test/lib/tres/handler_test.exs
Normal file
178
test/lib/tres/handler_test.exs
Normal file
|
|
@ -0,0 +1,178 @@
|
||||||
|
defmodule Tres.HanderTest do
|
||||||
|
use ExUnit.Case
|
||||||
|
|
||||||
|
@datapath_id "0000000000000001"
|
||||||
|
|
||||||
|
setup_all do
|
||||||
|
:ok = wait_datapath_is_connected()
|
||||||
|
:ok = send_message(Openflow.FlowMod.new(command: :delete, table_id: :all))
|
||||||
|
:ok = send_message(Openflow.GroupMod.new(command: :delete, group_id: :all))
|
||||||
|
:ok = send_message(Openflow.MeterMod.new(command: :delete, meter_id: :all))
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "Openflow RoleRequest message" do
|
||||||
|
test "with role and generation_id" do
|
||||||
|
send_message(Openflow.Role.Request.new(role: :nochange, generation_id: 1))
|
||||||
|
%Openflow.Role.Reply{} = get_message()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "Openflow standard PacketIn message" do
|
||||||
|
test "with arp_packet" do
|
||||||
|
send_message(
|
||||||
|
Openflow.PacketOut.new(
|
||||||
|
buffer_id: :no_buffer,
|
||||||
|
in_port: :controller,
|
||||||
|
actions: [Openflow.Action.Output.new(:controller)],
|
||||||
|
data: File.read!("test/packet_data/arp_packet.raw")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
%Openflow.PacketIn{} = get_message()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "Openflow Nicira PacketIn2 message" do
|
||||||
|
test "with arp_packet" do
|
||||||
|
send_message(Openflow.NxSetPacketInFormat.new(:nxt_packet_in2))
|
||||||
|
|
||||||
|
send_message(
|
||||||
|
Openflow.FlowMod.new(
|
||||||
|
instructions: Openflow.Instruction.ApplyActions.new(Openflow.Action.NxController2.new())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
send_message(
|
||||||
|
Openflow.PacketOut.new(
|
||||||
|
buffer_id: :no_buffer,
|
||||||
|
in_port: :controller,
|
||||||
|
actions: [Openflow.Action.Output.new(:controller)],
|
||||||
|
data: File.read!("test/packet_data/arp_packet.raw")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
%Openflow.NxPacketIn2{} = get_message()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "Openflow FlowRemoved message" do
|
||||||
|
test "with a flow" do
|
||||||
|
send_message(
|
||||||
|
Openflow.FlowMod.new(flags: [:send_flow_rem], match: Openflow.Match.new(reg0: 99))
|
||||||
|
)
|
||||||
|
|
||||||
|
send_message(Openflow.FlowMod.new(command: :delete, match: Openflow.Match.new(reg0: 99)))
|
||||||
|
%Openflow.FlowRemoved{} = get_message()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "Openflow AggregateStats Reply message" do
|
||||||
|
test "with no option" do
|
||||||
|
send_message(Openflow.Multipart.Aggregate.Request.new())
|
||||||
|
%Openflow.Multipart.Aggregate.Reply{} = get_message()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "Openflow DescStats Reply message" do
|
||||||
|
test "with no option" do
|
||||||
|
send_message(Openflow.Multipart.Desc.Request.new())
|
||||||
|
%Openflow.Multipart.Desc.Reply{} = get_message()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "Openflow FlowStats Reply message" do
|
||||||
|
test "with no option" do
|
||||||
|
send_message(Openflow.Multipart.Flow.Request.new())
|
||||||
|
%Openflow.Multipart.Flow.Reply{} = get_message()
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with 3000 flows" do
|
||||||
|
Enum.each(1..3000, fn n ->
|
||||||
|
send_message(Openflow.FlowMod.new(match: Openflow.Match.new(reg0: n)))
|
||||||
|
end)
|
||||||
|
|
||||||
|
send_message(Openflow.Multipart.Flow.Request.new())
|
||||||
|
%Openflow.Multipart.Flow.Reply{flags: [:more]} = get_message()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "Openflow GroupStats Reply message" do
|
||||||
|
test "with no option" do
|
||||||
|
send_message(Openflow.Multipart.Group.Request.new())
|
||||||
|
%Openflow.Multipart.Group.Reply{} = get_message()
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with 3000 groups" do
|
||||||
|
Enum.each(1..3000, fn n -> send_message(Openflow.GroupMod.new(group_id: n)) end)
|
||||||
|
send_message(Openflow.Multipart.Group.Request.new())
|
||||||
|
%Openflow.Multipart.Group.Reply{flags: [:more]} = get_message()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "Openflow GroupDesc Reply message" do
|
||||||
|
test "with no option" do
|
||||||
|
send_message(Openflow.Multipart.GroupDesc.Request.new())
|
||||||
|
%Openflow.Multipart.GroupDesc.Reply{} = get_message()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "Openflow GroupFeatures Reply message" do
|
||||||
|
test "with no option" do
|
||||||
|
send_message(Openflow.Multipart.GroupFeatures.Request.new())
|
||||||
|
%Openflow.Multipart.GroupFeatures.Reply{} = get_message()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "Openflow MeterStats Reply message" do
|
||||||
|
test "with no option" do
|
||||||
|
send_message(Openflow.Multipart.Meter.Request.new())
|
||||||
|
%Openflow.Multipart.Meter.Reply{} = get_message()
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with 3000 meters" do
|
||||||
|
Enum.each(1..3000, fn n ->
|
||||||
|
send_message(
|
||||||
|
Openflow.MeterMod.new(
|
||||||
|
meter_id: n,
|
||||||
|
flags: [:pktps, :burst, :stats],
|
||||||
|
bands: [Openflow.MeterBand.Drop.new(rate: 1000, burst_size: 10)]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end)
|
||||||
|
|
||||||
|
send_message(Openflow.Multipart.Meter.Request.new())
|
||||||
|
%Openflow.Multipart.Meter.Reply{flags: [:more]} = get_message()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "Openflow PortDescStats Reply message" do
|
||||||
|
test "with no option" do
|
||||||
|
send_message(Openflow.Multipart.PortDesc.Request.new())
|
||||||
|
%Openflow.Multipart.PortDesc.Reply{} = get_message()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "Openflow PortStats Reply message" do
|
||||||
|
test "with no option" do
|
||||||
|
send_message(Openflow.Multipart.Port.Request.new())
|
||||||
|
%Openflow.Multipart.Port.Reply{} = get_message()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# helper
|
||||||
|
|
||||||
|
def wait_datapath_is_connected do
|
||||||
|
case Tres.SwitchRegistry.lookup_handler_pid(@datapath_id) do
|
||||||
|
nil -> wait_datapath_is_connected()
|
||||||
|
pid when is_pid(pid) -> :ok
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def send_message(msg) do
|
||||||
|
:ok = Tres.ExampleHandler.send(@datapath_id, msg)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_message do
|
||||||
|
Tres.ExampleHandler.get(@datapath_id)
|
||||||
|
end
|
||||||
|
end
|
||||||
Loading…
Add table
Add a link
Reference in a new issue