Project

General

Profile

Download (9.32 KB) Statistics
| Branch: | Tag: | Revision:
d1e614b5 Marc Dequènes (Duck)
#--
# CyborgHood, a distributed system management software.
364e4a96 Marc Dequènes (Duck)
# Copyright (c) 2009-2011 Marc Dequènes (Duck) <Duck@DuckCorp.org>
d1e614b5 Marc Dequènes (Duck)
#
# 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 'singleton'
7466fc08 Marc Dequènes (Duck)
require 'ostruct'
d1e614b5 Marc Dequènes (Duck)

module CyborgHood
7466fc08 Marc Dequènes (Duck)
module DSL
class ServerApiNode < BaseDSL
4761cd13 Marc Dequènes (Duck)
attr_reader :bot, :node_name, :parent_node, :store
8fcaacd6 Marc Dequènes (Duck)
66cff75f Marc Dequènes (Duck)
include TaskAspect

7466fc08 Marc Dequènes (Duck)
# needed for testing node existence
reveal :nil?
4761cd13 Marc Dequènes (Duck)
reveal :respond_to?
476368e8 Marc Dequènes (Duck)
eab0c364 Marc Dequènes (Duck)
def initialize(bot, parent_node = nil, options = {}, &block)
7466fc08 Marc Dequènes (Duck)
@bot = bot
@parent_node = parent_node
eab0c364 Marc Dequènes (Duck)
@ldir = options[:dir]
@hidden = options[:hidden] || false
4761cd13 Marc Dequènes (Duck)
7466fc08 Marc Dequènes (Duck)
# don't call super because we need defered loading
4761cd13 Marc Dequènes (Duck)
@blocks = []
add_behavior(&block)

@ldir_loaded = false
a3483167 Marc Dequènes (Duck)
7466fc08 Marc Dequènes (Duck)
cleanup
a3483167 Marc Dequènes (Duck)
end
8fcaacd6 Marc Dequènes (Duck)
ec7296ea Marc Dequènes (Duck)
def root?
@parent_node.nil?
end

eab0c364 Marc Dequènes (Duck)
def hidden?
@hidden
end

ec7296ea Marc Dequènes (Duck)
def node_path
return '/' if root?

path = @parent_node.node_path
path += "/" + @node_name unless @node_name.empty?
path.gsub("//", "/")
end

7466fc08 Marc Dequènes (Duck)
def add_behavior(&block)
4761cd13 Marc Dequènes (Duck)
if block_given?
@blocks << block
else
return if @ldir_loaded

ec7296ea Marc Dequènes (Duck)
begin
Dir.glob(File.join(self.lpath, '*.rb')) do |file|
logger.debug "Interface: loading file '#{file}'"
begin
@blocks << File.read(file)
rescue
logger.error "Interface: definition file '#{file}' cannot be read"
end
end
rescue
logger.error "Interface: directory '#{self.lpath}' cannot be read"
4761cd13 Marc Dequènes (Duck)
end

@ldir_loaded = true
end
8fcaacd6 Marc Dequènes (Duck)
end

7466fc08 Marc Dequènes (Duck)
# string, array (useful for aliases), or regex
# TODO: name validation
eab0c364 Marc Dequènes (Duck)
def node(match, options = {}, &block)
child_node = self.class.new(@bot, self, options, &block)
7466fc08 Marc Dequènes (Duck)
if match.is_a? Array
match.each{|n| @nodes[n] = child_node}
else
@nodes[match] = child_node
end
8fcaacd6 Marc Dequènes (Duck)
end
69a12fdb Marc Dequènes (Duck)
7bfcd98b Marc Dequènes (Duck)
def attr_search_node(list = nil, &block)
list ||= block

eab0c364 Marc Dequènes (Duck)
lookup_node = self

node '?', :hidden => true do
on_request do |request|
if request.args.empty?
request.reply.results = lookup_node.__send__(:visible_nodes_names)
else
7bfcd98b Marc Dequènes (Duck)
request.reply.results = list.nil? ? search_among_children(request, lookup_node) :
search_in_list(request, lookup_node, list)
eab0c364 Marc Dequènes (Duck)
end
end
end
end

7466fc08 Marc Dequènes (Duck)
def on_request(&cb)
@request_cb = cb
69a12fdb Marc Dequènes (Duck)
end
d1e614b5 Marc Dequènes (Duck)
7466fc08 Marc Dequènes (Duck)
def _is_node?(session, node_path)
node = __send__(:find_node, session, node_path)
not node.nil?
end
69a12fdb Marc Dequènes (Duck)
7466fc08 Marc Dequènes (Duck)
def _call(session, node_path, args = nil)
args ||= []
7bfcd98b Marc Dequènes (Duck)
raise CyberError.new(:unrecoverable, 'api/cyborghood', "wrong format for arguments when calling node '#{node_path}'") unless args.is_a? Array
d1e614b5 Marc Dequènes (Duck)
7466fc08 Marc Dequènes (Duck)
node = find_node(session, node_path)
7bfcd98b Marc Dequènes (Duck)
raise CyberError.new(:unrecoverable, 'api/cyborghood', "unknown node '#{node_path}'") if node.nil?
d1e614b5 Marc Dequènes (Duck)
7466fc08 Marc Dequènes (Duck)
logger.debug "[Server API] Node '#{node_path}' found"
afdc8d97 Marc Dequènes (Duck)
node.__send__(:request, session, args)
7466fc08 Marc Dequènes (Duck)
end
23b2305f Marc Dequènes (Duck)
7466fc08 Marc Dequènes (Duck)
protected
23b2305f Marc Dequènes (Duck)
4761cd13 Marc Dequènes (Duck)
def base_lpath
File.join(Config::LIB_DIR, 'cyborghood-' + @bot.name.downcase, 'interface')
end

def lpath
parent_lpath = @parent_node.nil? ? self.base_lpath : @parent_node.lpath
@ldir.nil? ? parent_lpath : File.join(parent_lpath, @ldir)
end

7466fc08 Marc Dequènes (Duck)
def load(node_element = '')
cleanup
@node_name = node_element
@blocks.each do |bl|
ec7296ea Marc Dequènes (Duck)
begin
if bl.is_a? String
instance_eval bl
else
instance_eval &bl
end
rescue Exception => e
logger.error "Interface: a definition block for node '#{node_path}' is buggy: " + e.message
4761cd13 Marc Dequènes (Duck)
end
7466fc08 Marc Dequènes (Duck)
end
end
1c89625f Marc Dequènes (Duck)
7466fc08 Marc Dequènes (Duck)
def find_node(session, node_path)
4761cd13 Marc Dequènes (Duck)
# node_path is a string argument when interface root node is called, but is a list of node elements later on
7466fc08 Marc Dequènes (Duck)
if root?
logger.debug "[Server API] Looking for node '#{node_path}'"
node_path = node_path.split("/")
# remove empty string before first "/"
node_path.shift
# initial load
load
end
41802ec1 Marc Dequènes (Duck)
7466fc08 Marc Dequènes (Duck)
node_element = node_path.shift
logger.debug "[Server API] Looking for node element '#{node_element}'"
if node_element.nil?
return self
41802ec1 Marc Dequènes (Duck)
else
7466fc08 Marc Dequènes (Duck)
next_node = find_child_node(node_element)
if next_node
next_node.__send__(:load, node_element)
return next_node.__send__(:find_node, session, node_path)
else
return
end
41802ec1 Marc Dequènes (Duck)
end
end

7466fc08 Marc Dequènes (Duck)
def find_child_node(child_node)
return @nodes[child_node] if @nodes.has_key? child_node

@nodes.each_pair do |match, node|
found = if match.is_a? String
child_node == match
elsif match.is_a? Regexp
child_node =~ Regexp.new(match)
elsif match.is_a? Proc
match.call.include? child_node
end
return node if found
end
f94d64b1 Marc Dequènes (Duck)
7466fc08 Marc Dequènes (Duck)
nil
d1e614b5 Marc Dequènes (Duck)
end

7466fc08 Marc Dequènes (Duck)
class Request
attr_reader :session, :args, :reply
b7f7d214 Marc Dequènes (Duck)
eab0c364 Marc Dequènes (Duck)
def initialize(session, args = [])
7466fc08 Marc Dequènes (Duck)
@session = session
@args = args
b7f7d214 Marc Dequènes (Duck)
7466fc08 Marc Dequènes (Duck)
@reply = {
:results => {},
:infos => [],
:warnings => [],
:errors => []
}.to_ostruct
b7f7d214 Marc Dequènes (Duck)
end
end

eab0c364 Marc Dequènes (Duck)
def request(session, args = [])
7466fc08 Marc Dequènes (Duck)
if @request_cb
request = Request.new(session, args)
begin
@request_cb.call(request)
# TODO: full reply needed
request.reply.results
rescue
logger.debug "node request error message: " + $!
logger.debug "node request error backtrace: " + $!.backtrace.join("\n")
7bfcd98b Marc Dequènes (Duck)
raise CyberError.new(:unrecoverable, 'api/cyborghood', "call failed on node '#{node_path}': " + $!)
b7f7d214 Marc Dequènes (Duck)
end
7466fc08 Marc Dequènes (Duck)
else
eab0c364 Marc Dequènes (Duck)
visible_nodes_names
end
end

def visible_nodes
Hash[@nodes.select{|match, node| not node.hidden? }]
end

def node_match_to_name(match)
if match.is_a? String
match
elsif match.is_a? Regexp
'/' + match.to_s + '/'
elsif match.is_a? Proc
match.call
b7f7d214 Marc Dequènes (Duck)
end
end
d1e614b5 Marc Dequènes (Duck)
eab0c364 Marc Dequènes (Duck)
def visible_nodes_names
visible_nodes.keys.collect do |match|
node_match_to_name(match)
end.compact.flatten
end

7466fc08 Marc Dequènes (Duck)
def cleanup
@nodes = {}
@request_cb = nil
4761cd13 Marc Dequènes (Duck)
# data memorized during walk in the node tree
if root?
@store = OpenStruct.new
else
@store = @parent_node.store
end
a3483167 Marc Dequènes (Duck)
end
eab0c364 Marc Dequènes (Duck)
def hash_match_criterias(hash, crit)
crit.each do |key, wanted_value|
2c35cfa1 Marc Dequènes (Duck)
value = hash[key]
eab0c364 Marc Dequènes (Duck)
if wanted_value.is_a? Array
return false unless wanted_value.include?(value)
elsif wanted_value.is_a? Regexp
return false unless value =~ wanted_value
else
return false unless value == wanted_value
end
end

true
end
7bfcd98b Marc Dequènes (Duck)
def search_among_children(request, lookup_node)
ee2c32c3 Marc Dequènes (Duck)
criterias = request.args.first
return {} unless criterias.is_a? Hash

node_names_list = {}
7bfcd98b Marc Dequènes (Duck)
lookup_node.__send__(:visible_nodes).each do |match, node|
if match.is_a? String
match_list = [match]
elsif match.is_a? Proc
match_list = match.call
else
next
end

match_list.each do |child_node_name|
node.__send__(:load, child_node_name)
result = node.__send__(:request, request.session)
next unless result.respond_to? :to_hash

ee2c32c3 Marc Dequènes (Duck)
child_node_attrs = result.to_hash

if hash_match_criterias(child_node_attrs, criterias)
node_names_list[child_node_name] = child_node_attrs
7bfcd98b Marc Dequènes (Duck)
end
end
end

node_names_list
end

def search_in_list(request, lookup_node, list)
criterias = request.args.first
ee2c32c3 Marc Dequènes (Duck)
return {} unless criterias.is_a? Hash
7bfcd98b Marc Dequènes (Duck)
if list.is_a? Proc
ee2c32c3 Marc Dequènes (Duck)
data = list.call(criterias)

return data[:list] unless data[:post_filter]
7bfcd98b Marc Dequènes (Duck)
end

ee2c32c3 Marc Dequènes (Duck)
node_names_list = {}

data[:list].each do |element_name, element|
element_attrs = element.to_hash
7bfcd98b Marc Dequènes (Duck)
ee2c32c3 Marc Dequènes (Duck)
if hash_match_criterias(element_attrs, criterias)
node_names_list[element_name] = element_attrs
7bfcd98b Marc Dequènes (Duck)
end
end

node_names_list
end
a3483167 Marc Dequènes (Duck)
end
7bfcd98b Marc Dequènes (Duck)
end # DSL
d1e614b5 Marc Dequènes (Duck)
end