Add patch_panel example
This commit is contained in:
parent
b16cafbb5c
commit
51729542f3
12 changed files with 227 additions and 0 deletions
4
examples/patch_panel/.formatter.exs
Normal file
4
examples/patch_panel/.formatter.exs
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
# Used by "mix format"
|
||||||
|
[
|
||||||
|
inputs: ["mix.exs", "{config,lib,test}/**/*.{ex,exs}"]
|
||||||
|
]
|
||||||
24
examples/patch_panel/.gitignore
vendored
Normal file
24
examples/patch_panel/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
# The directory Mix will write compiled artifacts to.
|
||||||
|
/_build/
|
||||||
|
|
||||||
|
# If you run "mix test --cover", coverage assets end up here.
|
||||||
|
/cover/
|
||||||
|
|
||||||
|
# The directory Mix downloads your dependencies sources to.
|
||||||
|
/deps/
|
||||||
|
|
||||||
|
# Where 3rd-party dependencies like ExDoc output generated docs.
|
||||||
|
/doc/
|
||||||
|
|
||||||
|
# Ignore .fetch files in case you like to edit your project deps locally.
|
||||||
|
/.fetch
|
||||||
|
|
||||||
|
# If the VM crashes, it generates a dump, let's ignore it too.
|
||||||
|
erl_crash.dump
|
||||||
|
|
||||||
|
# Also ignore archive artifacts (built via "mix archive.build").
|
||||||
|
*.ez
|
||||||
|
|
||||||
|
# Ignore package tarball (built via "mix hex.build").
|
||||||
|
patch_panel-*.tar
|
||||||
|
|
||||||
16
examples/patch_panel/README.md
Normal file
16
examples/patch_panel/README.md
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
# PatchPanel
|
||||||
|
|
||||||
|
openflow controller that emulates a software patch panel
|
||||||
|
|
||||||
|
## prerequisites
|
||||||
|
|
||||||
|
- Erlang 20 or higher
|
||||||
|
- Elixir 1.6.1 or higher
|
||||||
|
- OpenFlow switch supports version 1.3
|
||||||
|
|
||||||
|
```iex
|
||||||
|
# To add a patch link
|
||||||
|
iex > :ok = PatchPanel.Openflow.Controller.create_patch("de780562fb45, 1, 2)
|
||||||
|
# To delete a patch link
|
||||||
|
iex > :ok = PatchPanel.Openflow.Controller.delete_patch("de780562fb45, 1, 2)
|
||||||
|
```
|
||||||
7
examples/patch_panel/config/config.exs
Normal file
7
examples/patch_panel/config/config.exs
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
# This file is responsible for configuring your application
|
||||||
|
# and its dependencies with the aid of the Mix.Config module.
|
||||||
|
use Mix.Config
|
||||||
|
|
||||||
|
config :tres,
|
||||||
|
callback_module: PatchPanel.Openflow.Controller,
|
||||||
|
callback_args: []
|
||||||
18
examples/patch_panel/lib/patch_panel.ex
Normal file
18
examples/patch_panel/lib/patch_panel.ex
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
defmodule PatchPanel do
|
||||||
|
@moduledoc """
|
||||||
|
Documentation for PatchPanel.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Hello world.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> PatchPanel.hello
|
||||||
|
:world
|
||||||
|
|
||||||
|
"""
|
||||||
|
def hello do
|
||||||
|
:world
|
||||||
|
end
|
||||||
|
end
|
||||||
20
examples/patch_panel/lib/patch_panel/application.ex
Normal file
20
examples/patch_panel/lib/patch_panel/application.ex
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
defmodule PatchPanel.Application do
|
||||||
|
# See https://hexdocs.pm/elixir/Application.html
|
||||||
|
# for more information on OTP Applications
|
||||||
|
@moduledoc false
|
||||||
|
|
||||||
|
use Application
|
||||||
|
|
||||||
|
alias PatchPanel.Openflow
|
||||||
|
|
||||||
|
def start(_type, _args) do
|
||||||
|
import Supervisor.Spec
|
||||||
|
|
||||||
|
children = [
|
||||||
|
worker(Registry, [[keys: :unique, name: Openflow.Registry]], id: Openflow.Registry),
|
||||||
|
]
|
||||||
|
|
||||||
|
opts = [strategy: :one_for_one, name: PatchPanel.Supervisor]
|
||||||
|
Supervisor.start_link(children, opts)
|
||||||
|
end
|
||||||
|
end
|
||||||
81
examples/patch_panel/lib/patch_panel/openflow/controller.ex
Normal file
81
examples/patch_panel/lib/patch_panel/openflow/controller.ex
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
defmodule PatchPanel.Openflow.Controller do
|
||||||
|
use GenServer
|
||||||
|
use Tres.Controller
|
||||||
|
|
||||||
|
import Logger
|
||||||
|
|
||||||
|
defmodule State do
|
||||||
|
defstruct [:datapath_id]
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_patch(datapath_id, port_a, port_b) do
|
||||||
|
case PatchPanel.Openflow.Registry.lookup_pid(datapath_id) do
|
||||||
|
nil ->
|
||||||
|
{:error, :not_found}
|
||||||
|
pid when is_pid(pid) ->
|
||||||
|
GenServer.call(pid, {:create, port_a, port_b})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete_patch(datapath_id, port_a, port_b) do
|
||||||
|
case PatchPanel.Openflow.Registry.lookup_pid(datapath_id) do
|
||||||
|
nil ->
|
||||||
|
{:error, :not_found}
|
||||||
|
pid when is_pid(pid) ->
|
||||||
|
GenServer.call(pid, {:delete, port_a, port_b})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def start_link({datapath_id, _aux_id}, _start_args) do
|
||||||
|
GenServer.start_link(__MODULE__, [datapath_id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def init([datapath_id]) do
|
||||||
|
:ok = info("Switch Connected: datapath_id = #{datapath_id}")
|
||||||
|
{:ok, _} = PatchPanel.Openflow.Registry.register(datapath_id)
|
||||||
|
{:ok, %State{datapath_id: datapath_id}}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_call({:create, port_a, port_b}, _from, %State{datapath_id: datapath_id} = state) do
|
||||||
|
:ok = add_flow_entries(datapath_id, port_a, port_b)
|
||||||
|
{:reply, :ok, state}
|
||||||
|
end
|
||||||
|
def handle_call({:delete, port_a, port_b}, _from, %State{datapath_id: datapath_id} = state) do
|
||||||
|
:ok = del_flow_entries(datapath_id, port_a, port_b)
|
||||||
|
{:reply, :ok, state}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_info({:switch_disconnected, reason}, %State{datapath_id: datapath_id} = state) do
|
||||||
|
:ok = warn("#{datapath_id} disconnected: reason = #{inspect(reason)}")
|
||||||
|
:ok = PatchPanel.Openflow.Registry.unregister(datapath_id)
|
||||||
|
{:stop, :normal, state}
|
||||||
|
end
|
||||||
|
def handle_info(_info, state) do
|
||||||
|
{:noreply, state}
|
||||||
|
end
|
||||||
|
|
||||||
|
def terminate(reason, state) do
|
||||||
|
{reason, state}
|
||||||
|
end
|
||||||
|
|
||||||
|
# private functions
|
||||||
|
|
||||||
|
defp add_flow_entries(datapath_id, port_a, port_b) do
|
||||||
|
:ok = send_flow_mod_add(
|
||||||
|
datapath_id,
|
||||||
|
match: Match.new(in_port: port_a),
|
||||||
|
instructions: [ApplyActions.new(Output.new(port_b))]
|
||||||
|
)
|
||||||
|
|
||||||
|
:ok = send_flow_mod_add(
|
||||||
|
datapath_id,
|
||||||
|
match: Match.new(in_port: port_b),
|
||||||
|
instructions: [ApplyActions.new(Output.new(port_a))]
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp del_flow_entries(datapath_id, port_a, port_b) do
|
||||||
|
:ok = send_flow_mod_delete(datapath_id, match: Match.new(in_port: port_a))
|
||||||
|
:ok = send_flow_mod_delete(datapath_id, match: Match.new(in_port: port_b))
|
||||||
|
end
|
||||||
|
end
|
||||||
16
examples/patch_panel/lib/patch_panel/openflow/registry.ex
Normal file
16
examples/patch_panel/lib/patch_panel/openflow/registry.ex
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
defmodule PatchPanel.Openflow.Registry do
|
||||||
|
def register(datapath_id) do
|
||||||
|
{:ok, _} = Registry.register(__MODULE__, datapath_id, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def unregister(datapath_id) do
|
||||||
|
:ok = Registry.unregister(__MODULE__, datapath_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def lookup_pid(datapath_id) do
|
||||||
|
case Registry.lookup(__MODULE__, datapath_id) do
|
||||||
|
[{pid, _}] -> pid
|
||||||
|
[] -> nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
26
examples/patch_panel/mix.exs
Normal file
26
examples/patch_panel/mix.exs
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
defmodule PatchPanel.MixProject do
|
||||||
|
use Mix.Project
|
||||||
|
|
||||||
|
def project do
|
||||||
|
[
|
||||||
|
app: :patch_panel,
|
||||||
|
version: "0.1.0",
|
||||||
|
elixir: "~> 1.6",
|
||||||
|
start_permanent: Mix.env() == :prod,
|
||||||
|
deps: deps()
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Run "mix help compile.app" to learn about applications.
|
||||||
|
def application do
|
||||||
|
[
|
||||||
|
extra_applications: [:logger],
|
||||||
|
mod: {PatchPanel.Application, []}
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Run "mix help deps" to learn about dependencies.
|
||||||
|
defp deps do
|
||||||
|
[{:tres, path: "../../../tres"}]
|
||||||
|
end
|
||||||
|
end
|
||||||
6
examples/patch_panel/mix.lock
Normal file
6
examples/patch_panel/mix.lock
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
%{
|
||||||
|
"eovsdb": {:git, "https://github.com/shun159/eovsdb.git", "1ff1572708d72fd25631c681f2102407903252a3", [branch: "master"]},
|
||||||
|
"jsone": {:git, "https://github.com/sile/jsone.git", "eecc9666c7165e1870b78a7a762549ae8d1c391b", [tag: "1.2.1"]},
|
||||||
|
"ranch": {:hex, :ranch, "1.4.0", "10272f95da79340fa7e8774ba7930b901713d272905d0012b06ca6d994f8826b", [:rebar3], [], "hexpm"},
|
||||||
|
"uuid": {:git, "https://github.com/avtobiff/erlang-uuid.git", "585c2474afb4a597ae8c8bf6d21e5a9c73f18e0b", [tag: "v0.5.0"]},
|
||||||
|
}
|
||||||
8
examples/patch_panel/test/patch_panel_test.exs
Normal file
8
examples/patch_panel/test/patch_panel_test.exs
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
defmodule PatchPanelTest do
|
||||||
|
use ExUnit.Case
|
||||||
|
doctest PatchPanel
|
||||||
|
|
||||||
|
test "greets the world" do
|
||||||
|
assert PatchPanel.hello() == :world
|
||||||
|
end
|
||||||
|
end
|
||||||
1
examples/patch_panel/test/test_helper.exs
Normal file
1
examples/patch_panel/test/test_helper.exs
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
ExUnit.start()
|
||||||
Loading…
Add table
Add a link
Reference in a new issue