Project

General

Profile

« Previous | Next » 

Revision b7f7d214

Added by Marc Dequènes almost 14 years ago

  • ID b7f7d214220c8cb982ed8e5dfdb68bd7a199e1d0

[evol] MapMaker / Cyborg server interface: cleanup, reorg, ...

View differences:

bin/mapmaker
class MapMakerInterface
include CyborgServerInterface
include CyborgServerDefaultInterface
include CyborgServerRootInterfaceAddon
class DNS < Services::DNS::System
include CyborgServerInterface
......
"coucou: " + data.inspect
end
class Zones
class Taiste
include CyborgServerInterface
def api_methods
["coucou", "toto", "plop"]
end
dynamic_interface {|node_name| ">>> #{node_name} <<<" }
end
class Zones
include CyborgServerStatefulInterface
def initialize
......
@dns.zones
end
def method_missing(method_name, *args)
session = args.shift
zone_name = method_name.to_s
if api_methods.include?(zone_name)
resource_key = "dns/zones/#{zone_name}"
session.store.get(resource_key) { Zone.new(zone_name) }
else
super
end
end
stateful_dynamic_interface("dns/zones/#NODE#") {|node_name| Zone.new(node_name) }
class Zone < Services::DNS::Zone
include CyborgServerEmbeddedInterface
lib/cyborghood/cyborg/conversation.rb
#--
# CyborgHood, a distributed system management software.
# Copyright (c) 2009-2010 Marc Dequènes (Duck) <Duck@DuckCorp.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#++
require 'cyborghood/cyborg/session'
module CyborgHood
class Conversation < EventMachine::Protocols::LineAndTextProtocol
private_class_method :new
# don't rely on EventMachine's default, it may change one day
MaxLineLength = 16*1024
EOD = "\033[0J"
COMMAND_PATTERN = "^#{CyborgServerInterfaceBase::NODE_PATTERN}(?: ([+?]+))?$"
def initialize(interface)
@interface = interface
super
@config = Config.instance
@split_data_mode = false
@split_data_cmd = nil
@split_data = []
@session = Session.new
end
def send_line(msg)
send_data "#{msg}\n"
logger.debug "Sent data [#{identifier}]: #{msg}"
end
def post_init
logger.debug "New conversation with #{identifier}"
send_line "220 #{PRODUCT} #{VERSION} - #{@config.bot_name}"
end
def receive_line(data)
return if data.empty?
if data == EOD
logger.debug "Received EOD [#{identifier}]"
exit_split_mode
else
if @split_data_mode
logger.debug "Received data (split mode) [#{identifier}]: #{data}"
@split_data << data
else
logger.debug "Received data [#{identifier}]: #{data}"
unless data =~ Regexp.new(COMMAND_PATTERN)
logger.error "Error [#{identifier}]: syntax error"
send_line "552 syntax error in command"
return
end
cmd = $1
flags = $2 || ""
if flags.index '?'
send_line "250+ ok"
send_line({'exists' => @interface.has_node?(cmd)}.to_yaml)
return
end
if flags.index '+'
enter_split_mode(cmd)
else
receive_command(cmd)
end
end
end
end
def receive_command(cmd, data = nil)
logger.debug "Executing command '#{cmd}' [#{identifier}]"
send_line @interface.call(@session, cmd, data)
end
def receive_error(msg)
logger.error "Error [#{identifier}]: #{msg}"
end
def unbind
logger.debug "Conversation finished with #{identifier}"
@session.clear
end
protected
def enter_split_mode(cmd)
if @split_data_mode
logger.error "Error [#{identifier}]: already in split mode"
send_line "551 protocol error"
@split_data_mode = false
@split_data_cmd = nil
else
logger.debug "Entered split mode for command '#{cmd}' [#{identifier}]"
@split_data_mode = true
@split_data_cmd = cmd
end
@split_data = []
end
def exit_split_mode
if @split_data_mode
logger.debug "Quit split mode for command '#{@split_data_cmd}' [#{identifier}]"
receive_command(@split_data_cmd, @split_data.join("\n"))
else
logger.error "Error [#{identifier}]: not in split mode"
send_line "551 protocol error"
end
@split_data_mode = false
@split_data_cmd = nil
@split_data = []
end
end
class ConversationUNIXSocket < Conversation
public_class_method :new
def identifier
"unix_socket/#{@signature}"
end
end
end
lib/cyborghood/cyborg/interface.rb
module CyborgHood
module CyborgServerInterface
def self.included(base)
base.class_eval("include CyborgServerInterfaceBase")
base.class_eval("include Singleton")
end
end
module CyborgServerEmbeddedInterface
def self.included(base)
base.class_eval("include CyborgServerInterfaceBase")
base.export_parent_methods
end
end
module CyborgServerStatefulInterface
end
# the base mixin (not intended to be used directly, but...)
module CyborgServerInterfaceBase
NODE_PATTERN = "((?:\/|(?:\/[a-zA-Z0-9._]+)+[?=]?))"
......
end
end
module CyborgServerDefaultInterface
# structural mixins
module CyborgServerInterface
def self.included(base)
base.class_eval("include CyborgServerInterfaceBase")
base.class_eval("include Singleton")
base.extend(ClassMethods)
end
module ClassMethods
def dynamic_interface(&resource_generator)
class_eval do
class_inheritable_reader :resource_generator
def method_missing(method_name, *args)
node_name = method_name.to_s
if api_methods.include?(node_name)
self.resource_generator.call(node_name)
else
super
end
end
end
write_inheritable_attribute(:resource_generator, resource_generator)
end
end
end
module CyborgServerEmbeddedInterface
def self.included(base)
base.class_eval("include CyborgServerInterfaceBase")
base.export_parent_methods
end
end
module CyborgServerStatefulInterface
def self.included(base)
base.class_eval("include CyborgServerInterfaceBase")
base.extend(ClassMethods)
end
module ClassMethods
def stateful_dynamic_interface(resource_key_pattern, &resource_generator)
class_eval do
class_inheritable_reader :resource_key_pattern, :resource_generator
def method_missing(method_name, *args)
session = args.shift
node_name = method_name.to_s
if api_methods.include?(node_name)
resource_key = self.resource_key_pattern.gsub("#NODE#", node_name)
session.store.get(resource_key) { self.resource_generator.call(node_name) }
else
super
end
end
end
write_inheritable_attribute(:resource_key_pattern, resource_key_pattern)
write_inheritable_attribute(:resource_generator, resource_generator)
end
end
end
# additional mixin
module CyborgServerRootInterfaceAddon
PROTOCOL_VERSION = "0.1~"
def self.included(base)
lib/cyborghood/cyborg/server.rb
require 'eventmachine'
require 'cyborghood/cyborg/interface'
require 'shellwords'
require 'cyborghood/cyborg/conversation'
module CyborgHood
......
end
end
class Conversation < EventMachine::Protocols::LineAndTextProtocol
private_class_method :new
# don't rely on EventMachine's default, it may change one day
MaxLineLength = 16*1024
EOD = "\033[0J"
COMMAND_PATTERN = "^#{CyborgServerInterfaceBase::NODE_PATTERN}(?: ([+?]+))?$"
def initialize(interface)
@interface = interface
super
@config = Config.instance
@split_data_mode = false
@split_data_cmd = nil
@split_data = []
@session = Session.new
end
def send_line(msg)
send_data "#{msg}\n"
logger.debug "Sent data [#{identifier}]: #{msg}"
end
def post_init
logger.debug "New conversation with #{identifier}"
send_line "220 #{PRODUCT} #{VERSION} - #{@config.bot_name}"
end
def receive_line(data)
return if data.empty?
if data == EOD
logger.debug "Received EOD [#{identifier}]"
exit_split_mode
else
if @split_data_mode
logger.debug "Received data (split mode) [#{identifier}]: #{data}"
@split_data << data
else
logger.debug "Received data [#{identifier}]: #{data}"
unless data =~ Regexp.new(COMMAND_PATTERN)
logger.error "Error [#{identifier}]: syntax error"
send_line "552 syntax error in command"
return
end
cmd = $1
flags = $2 || ""
if flags.index '?'
send_line "250+ ok"
send_line({'exists' => @interface.has_node?(cmd)}.to_yaml)
return
end
if flags.index '+'
enter_split_mode(cmd)
else
receive_command(cmd)
end
end
end
end
def receive_command(cmd, data = nil)
logger.debug "Executing command '#{cmd}' [#{identifier}]"
send_line @interface.call(@session, cmd, data)
end
def receive_error(msg)
logger.error "Error [#{identifier}]: #{msg}"
end
def unbind
logger.debug "Conversation finished with #{identifier}"
@session.clear
end
protected
def enter_split_mode(cmd)
if @split_data_mode
logger.error "Error [#{identifier}]: already in split mode"
send_line "551 protocol error"
@split_data_mode = false
@split_data_cmd = nil
else
logger.debug "Entered split mode for command '#{cmd}' [#{identifier}]"
@split_data_mode = true
@split_data_cmd = cmd
end
@split_data = []
end
def exit_split_mode
if @split_data_mode
logger.debug "Quit split mode for command '#{@split_data_cmd}' [#{identifier}]"
receive_command(@split_data_cmd, @split_data.join("\n"))
else
logger.error "Error [#{identifier}]: not in split mode"
send_line "551 protocol error"
end
@split_data_mode = false
@split_data_cmd = nil
@split_data = []
end
end
class ConversationUNIXSocket < Conversation
public_class_method :new
def identifier
"unix_socket/#{@signature}"
end
end
# default interface if not overridden
class EmptyInterface
include CyborgServerInterface
include CyborgServerDefaultInterface
include CyborgServerRootInterfaceAddon
end
module SimpleServer
......
EmptyInterface.instance
end
end
class Session
attr_reader :store
def initialize
@store = Store.new
end
def clear
@store.values.each do |obj|
obj.__destroy if obj.respond_to? :__destroy
end
@store.clear
end
class Store < Hash
def get(key)
obj = self[key]
if obj.nil? and block_given?
obj = yield
self[key] = obj
end
obj
end
end
end
end
lib/cyborghood/cyborg/session.rb
#--
# CyborgHood, a distributed system management software.
# Copyright (c) 2009-2010 Marc Dequènes (Duck) <Duck@DuckCorp.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#++
module CyborgHood
class Session
attr_reader :store
def initialize
@store = Store.new
end
def clear
@store.values.each do |obj|
obj.__destroy if obj.respond_to? :__destroy
end
@store.clear
end
class Store < Hash
def get(key)
obj = self[key]
if obj.nil? and block_given?
obj = yield
self[key] = obj
end
obj
end
end
end
end

Also available in: Unified diff