OVSDB

Hex.pmHex DocsLicense

A pure-Elixir implementation of the Open vSwitch Database Management Protocol (RFC 7047).

Provides both client (IDL-style in-memory replica) and server (accept connections, handle RPCs) implementations for any application that needs to speak OVSDB — configuration management of Open vSwitch instances, SDN controller protocols like OpenSync, or any other OVSDB-compatible database.

Features

Status

Active development. The 0.2 line is feature-complete for client and server use; APIs are stable but may evolve before 1.0. All quality gates (format, credo strict, compile warnings-as-errors, dialyzer) pass cleanly. Live-validated end-to-end in iex. An ExUnit test suite is planned for a subsequent release.

Installation

Add ovsdb_ex to your mix.exs:

def deps do
  [
    {:ovsdb_ex, "~> 0.2"}
  ]
end

Quick start

Client: talk to an existing OVSDB server

alias OVSDB.{ClientSession, Transaction, Operation, Condition, UUID}

{:ok, session} = ClientSession.connect("ovsdb.local", 6640)

# List databases
{:ok, dbs} = ClientSession.list_dbs(session)

# Fetch schema
{:ok, schema_json} = ClientSession.get_schema(session, "Open_vSwitch")

# Build and send a transaction
txn =
  Transaction.new("Open_vSwitch")
  |> Transaction.add(
    Operation.insert("Bridge", %{"name" => "br-int"})
  )

{:ok, [%{"uuid" => ["uuid", new_uuid]}]} =
  ClientSession.transact(session, txn)

IDL: maintain a live replica

alias OVSDB.{ClientSession, Idl, Schema, SchemaHelper}

{:ok, session} = ClientSession.connect("ovsdb.local", 6640)
{:ok, schema_json} = ClientSession.get_schema(session, "Open_vSwitch")
{:ok, schema} = Schema.parse(schema_json)

# Register interest in specific tables/columns
helper =
  SchemaHelper.new(schema)
  |> SchemaHelper.register_columns!("AWLAN_Node", ["manager_addr"])
  |> SchemaHelper.register_table!("Wifi_VIF_State")

# Start the IDL — subscribes and populates from current state
{:ok, idl} = Idl.start_link(
  session: session,
  helper: helper,
  monitor_id: "my-idl"
)

# Subscribe to change notifications
Idl.subscribe(idl, "AWLAN_Node")

# Read any time, with no roundtrip
rows = Idl.get_table(idl, "AWLAN_Node")

# Receive {:idl_changed, idl, table, :insert | :modify | :delete, uuid}
# messages whenever the replica changes

Server: serve your own OVSDB-compatible database

defmodule MyHandler do
  @behaviour OVSDB.ServerSession.Handler

  def init(_opts), do: {:ok, %{rows: %{}}}

  def handle_list_dbs(state), do: {:ok, ["My_DB"], state}

  def handle_get_schema("My_DB", state), do: {:ok, my_schema(), state}

  def handle_transact("My_DB", ops, state) do
    {results, new_state} = apply_ops(ops, state)
    {:ok, results, new_state}
  end

  defp my_schema, do: %{"name" => "My_DB", "tables" => %{...}}
  defp apply_ops(_ops, state), do: {[], state}
end

# Start a server on port 6640
{:ok, _srv} = OVSDB.Server.start_link(
  port: 6640,
  handler: MyHandler
)

Working with OVSDB values

OVSDB's atomic types map to Elixir's native types. Only UUIDs need wrapping; sets and maps get small structs for the compound types:

alias OVSDB.{UUID, Set, Map, Value}

# Atomic types are native
42            # integer
3.14          # real
true          # boolean
"hello"       # string

# UUIDs wrap — raw strings can't be distinguished from other strings
uuid = UUID.generate()
#=> %OVSDB.UUID{value: "b7c5ef91-3a64-42d1-8a5c-f9e1d2a3b4c5"}

# Sets are unordered; 1-element sets optimize to bare value on the wire
ports = Set.new([uuid1, uuid2])

# Maps preserve ordered {k, v} entries (matching wire structure)
tags = Map.new(%{"owner" => "opensync", "role" => "ap"})

# Value.encode/1 walks any value, handling nested wrappers
Value.encode(ports)
#=> ["set", [["uuid", "..."], ["uuid", "..."]]]

Architecture

The library is layered:

Layer Modules Role
1. Data model UUID, NamedUUID, Set, Map, Row, Value Typed representations of RFC 7047 values
2. Protocol Protocol JSON-RPC 1.0 envelopes, wire serialization, classification
3. Operations Condition, Operation, Transaction, MonitorSpec, Schema, SchemaHelper High-level builders for RFC 7047 operations and schemas
4. Sessions Framer, Transport, ClientSession, ServerSession, Server, Idl Wire framing, socket ownership, request correlation, IDL replica

Each layer uses only the layers below it. The lower layers are usable standalone — you can build wire messages by hand, frame your own byte streams, or construct operations without a session if you prefer.

Alternatives

Contributing

Issues and pull requests welcome. The library is part of a broader effort to build SDN tooling (OpenSync controllers in particular) in Elixir — feedback from users of other OVSDB-based protocols is especially valuable.

License

Apache License 2.0. See LICENSE.