class Trema::Controller

The base class of Trema controller. Subclass and override handlers to implement a custom OpenFlow controller.

rubocop:disable ClassLength

Constants

DEFAULT_TCP_PORT
SWITCH

Attributes

logger[R]

@return [Logger]

Public Class Methods

_handler(_name) click to toggle source

@private Just a placeholder for YARD.

# File lib/trema/controller.rb, line 205
def self._handler(_name)
  # Noop.
end
create(port_number = DEFAULT_TCP_PORT, logging_level = ::Logger::INFO) click to toggle source

@private

# File lib/trema/controller.rb, line 133
def self.create(port_number = DEFAULT_TCP_PORT,
                logging_level = ::Logger::INFO)
  unless @controller_klass
    raise NoControllerDefined, 'No controller class is defined.'
  end
  @controller_klass.new(port_number, logging_level)
end
inherited(subclass) click to toggle source

@private

# File lib/trema/controller.rb, line 128
def self.inherited(subclass)
  @controller_klass = subclass
end
new(port_number = DEFAULT_TCP_PORT, logging_level = ::Logger::INFO) click to toggle source

@private

# File lib/trema/controller.rb, line 142
def initialize(port_number = DEFAULT_TCP_PORT,
               logging_level = ::Logger::INFO)
  @port_number = port_number
  @threads = []
  @logger = Logger.new(name)
  @logger.level = logging_level
end
timer_event(handler, options) click to toggle source
# File lib/trema/controller.rb, line 118
def self.timer_event(handler, options)
  @timer_handlers ||= {}
  @timer_handlers[handler] = options.fetch(:interval)
end
timer_handlers() click to toggle source
# File lib/trema/controller.rb, line 123
def self.timer_handlers
  @timer_handlers || {}
end

Public Instance Methods

echo_request(datapath_id, message) click to toggle source

The default handler for echo request messages. Override this to implement a custom handler.

# File lib/trema/controller.rb, line 223
def echo_request(datapath_id, message)
  echo_reply = Echo::Reply.new(transaction_id: message.xid)
  send_message datapath_id, echo_reply
end
name() click to toggle source
# File lib/trema/controller.rb, line 161
def name
  self.class.name
end
run(args) click to toggle source

@private Starts this controller. Usually you do not need to invoke explicitly, because this is called implicitly by “trema run” command.

# File lib/trema/controller.rb, line 154
def run(args)
  maybe_send_handler :start, args
  socket = TCPServer.open('<any>', @port_number)
  start_timers
  loop { start_switch_thread(socket.accept) }
end
send_flow_mod_add(datapath_id, options) click to toggle source

@!group OpenFlow Message

# File lib/trema/controller.rb, line 171
def send_flow_mod_add(datapath_id, options)
  flow_mod =
    case Pio::OpenFlow.version
    when 'OpenFlow10'
      FlowMod.new(FlowModAddOption.new(options).to_hash)
    when 'OpenFlow13'
      FlowMod.new(FlowModAdd13Option.new(options).to_hash)
    else
      raise "Unsupported OpenFlow version: #{Pio::OpenFlow.version}"
    end
  send_message datapath_id, flow_mod
end
send_flow_mod_delete(datapath_id, options) click to toggle source
# File lib/trema/controller.rb, line 184
def send_flow_mod_delete(datapath_id, options)
  flow_mod = FlowMod.new(FlowModDeleteOption.new(options).to_hash)
  send_message datapath_id, flow_mod
end
send_message(datapath_id, message) click to toggle source
# File lib/trema/controller.rb, line 194
def send_message(datapath_id, message)
  SWITCH.fetch(datapath_id).write message
rescue KeyError, Errno::ECONNRESET, Errno::EPIPE
  logger.debug "Switch #{datapath_id} is disconnected."
end
send_packet_out(datapath_id, options) click to toggle source
# File lib/trema/controller.rb, line 189
def send_packet_out(datapath_id, options)
  packet_out = PacketOut.new(PacketOutOption.new(options).to_hash)
  send_message datapath_id, packet_out
end
stop() click to toggle source
# File lib/trema/controller.rb, line 165
def stop
  @threads.map(&:kill)
end

Private Instance Methods

create_and_register_new_switch(socket) click to toggle source
# File lib/trema/controller.rb, line 267
def create_and_register_new_switch(socket)
  switch = Switch.new(socket)
  switch.init
  SWITCH[switch.datapath_id] = switch
rescue Switch::InitError
  error_message = switch.error_message
  case error_message
  when OpenFlow10::Error::HelloFailed, OpenFlow13::Error::HelloFailed
    maybe_send_handler :hello_failed, error_message
    raise $ERROR_INFO
  end
end
handle_openflow_message(datapath_id) click to toggle source

rubocop:disable MethodLength rubocop:disable AbcSize rubocop:disable CyclomaticComplexity

# File lib/trema/controller.rb, line 288
def handle_openflow_message(datapath_id)
  begin
    message = SWITCH.fetch(datapath_id).read
  rescue KeyError
    logger.debug "Switch #{datapath_id} is disconnected."
  end

  case message
  when Echo::Request
    maybe_send_handler :echo_request, datapath_id, message
  when Echo::Reply
    maybe_send_handler :echo_reply, datapath_id, message
  when Features::Reply
    maybe_send_handler :features_reply, datapath_id, message
  when PacketIn
    message.datapath_id = datapath_id
    maybe_send_handler :packet_in, datapath_id, message
  when PortStatus
    message.datapath_id = datapath_id
    case message.reason
    when :add
      maybe_send_handler :port_add, datapath_id, message
    when :delete
      maybe_send_handler :port_delete, datapath_id, message
    when :modify
      maybe_send_handler :port_modify, datapath_id, message
    else
      raise "Invalid Port Status message: #{message.inspect}"
    end
  when Barrier::Reply
    maybe_send_handler :barrier_reply, datapath_id, message
  when DescriptionStats::Reply
    maybe_send_handler :description_stats_reply, datapath_id, message
  else
    raise "Unknown OpenFlow message: #{message.inspect}"
  end
end
maybe_send_handler(handler, *args) click to toggle source
# File lib/trema/controller.rb, line 334
def maybe_send_handler(handler, *args)
  return unless respond_to?(handler)
  send_handler(handler, *args)
end
send_handler(handler, *args) click to toggle source

rubocop:enable MethodLength rubocop:enable AbcSize rubocop:enable CyclomaticComplexity

# File lib/trema/controller.rb, line 329
def send_handler(handler, *args)
  @handler_mutex ||= Mutex.new
  @handler_mutex.synchronize { __send__(handler, *args) }
end
start_switch_main(datapath_id) click to toggle source
# File lib/trema/controller.rb, line 260
def start_switch_main(datapath_id)
  maybe_send_handler :switch_ready, datapath_id
  loop { handle_openflow_message datapath_id }
rescue EOFError, IOError
  unregister_switch datapath_id
end
start_switch_thread(socket) click to toggle source
# File lib/trema/controller.rb, line 251
def start_switch_thread(socket)
  th = Thread.start(socket) do |sock|
    switch = create_and_register_new_switch(sock)
    start_switch_main switch.datapath_id
  end
  th.abort_on_exception = true
  @threads << th
end
start_timers() click to toggle source

@!endgroup

# File lib/trema/controller.rb, line 238
def start_timers
  self.class.timer_handlers.each do |handler, interval|
    th = Thread.start(handler, interval) do |method, sec|
      loop do
        send_handler method
        sleep sec
      end
    end
    th.abort_on_exception = true
    @threads << th
  end
end
unregister_switch(datapath_id) click to toggle source
# File lib/trema/controller.rb, line 280
def unregister_switch(datapath_id)
  SWITCH.delete datapath_id
  maybe_send_handler :switch_disconnected, datapath_id
end