Add patch_panel example

This commit is contained in:
Eishun Kondoh 2018-02-25 23:44:58 +09:00
parent b16cafbb5c
commit 51729542f3
12 changed files with 227 additions and 0 deletions

View 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
View 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

View 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)
```

View 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: []

View 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

View 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

View 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

View 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

View 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

View 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"]},
}

View file

@ -0,0 +1,8 @@
defmodule PatchPanelTest do
use ExUnit.Case
doctest PatchPanel
test "greets the world" do
assert PatchPanel.hello() == :world
end
end

View file

@ -0,0 +1 @@
ExUnit.start()