lib/ardtweeno/dispatcher.rb in ardtweeno-0.2.5 vs lib/ardtweeno/dispatcher.rb in ardtweeno-0.3.0

- old
+ new

@@ -1,10 +1,10 @@ #################################################################################################### -# @author David Kirwan <davidkirwanirl@gmail.com> -# @description Dispatcher system for the Ardtweeno Mesh Network +# @author David Kirwan https://github.com/davidkirwan/ardtweeno +# @description Ardtweeno dispatcher system # -# @date 30-03-2013 +# @date 14-06-2013 #################################################################################################### # Imports require 'rubygems' require 'serialport' @@ -22,11 +22,11 @@ # class Dispatcher include Singleton - attr_accessor :nodeManager, :parser, :confdata, :nodedata, :db, :auth, :coll, :log, :running + attr_accessor :nodeManager, :parser, :confdata, :nodedata, :db, :auth, :coll, :log, :running, :posts ## # Constructor for the Ardtweeno::Dispatcher class # @@ -41,11 +41,13 @@ @log.level = Ardtweeno.options[:level] ||= Logger::DEBUG @running = false @parser = nil - unless Ardtweeno.options[:test] ||= false + if Ardtweeno.options[:test] + @confdata = Ardtweeno.options[:confdata] + else @log.debug "Calling bootstrap()" bootstrap() end end @@ -199,32 +201,49 @@ ## # Ardtweeno::Dispatcher#addWatch method to add a watch on a node # # * *Args* : - # - ++ -> params Hash + # - ++ -> params Hash containing: {:node String name of the node, + # :notifyURL String URL to post a push notification to + # :method String either GET or PUSH to indicate HTTP methods + # :timeout Fixnum the timeout in seconds between push notifications } # * *Returns* : # - # * *Raises* : - # + # Ardtweeno::InvalidWatch if params do not adhere to specification + # Ardtweeno::AlreadyWatched if node is already on a watchlist + # def addWatch(params) begin apitimer = Time.now - if params.has_key? "node" and - params.has_key? "notifyURL" and - params.has_key? "method" and - params.has_key? "timeout" - + if params.has_key? :node and + params.has_key? :notifyURL and + params.has_key? :method and + params.has_key? :timeout + + + unless params[:method] == "GET" or params[:method] == "POST" + raise Ardtweeno::InvalidWatch, "Invalid Parameters" + end + + unless params[:timeout] >= 0 + raise Ardtweeno::InvalidWatch, "Invalid Parameters" + end + @log.debug "Watch API call seems valid, passing to NodeManager" @nodeManager.addWatch(params) else - raise Exception, "Invalid Parameters" + raise Ardtweeno::InvalidWatch, "Invalid Parameters" end @log.debug "Duration: #{Time.now - apitimer} seconds" - rescue Exception => e + + rescue Ardtweeno::AlreadyWatched => e + raise e, "This node already has a watch associated with it" + rescue Ardtweeno::InvalidWatch => e raise e end end @@ -241,13 +260,15 @@ begin @log.debug "Payload recieved, processing.." payload = JSON.parse(origionalPayload) - if payload["data"].nil? then raise Ardtweeno::InvalidData, "Packet missing data" end + if payload["data"].nil? then raise Ardtweeno::InvalidData, "Packet missing data"; end @log.debug "Payload contains a :data key, continuing.." - if payload["key"].nil? then raise Ardtweeno::InvalidData, "Packet missing key" end + if payload["data"].empty? then raise Ardtweeno::InvalidData, "Packet data empty"; end + @log.debug "Payload data is not empty, continuing.." + if payload["key"].nil? then raise Ardtweeno::InvalidData, "Packet missing key"; end @log.debug "Payload contains a :key key, continuing.." @log.debug "Searching for the corresponding Ardtweeno::Node in the system.." node = @nodeManager.search({:key=>payload["key"]}) @log.debug "This packet belongs to a valid node.." @@ -299,16 +320,34 @@ dev = @confdata["dev"] speed = @confdata["speed"] key = @confdata["adminkey"] - cmd = "/bin/bash -c '/usr/local/bin/node resources/serialparser.js #{dev} #{speed} #{key}'" - - @parser = fork do - Signal.trap("SIGTERM") { `killall node`; exit } - `#{cmd}` + begin + serialparser = Ardtweeno::SerialParser.new(dev, speed, 100, {:log=>@log, :level=>@log.level}) + rescue Exception => e + @log.fatal "Ardtweeno::Dispatcher#start Fatal Error constructing the SerialParser:" + @running = false + return false end + + @parser = Thread.new do + + begin + loop do + serialparser.listen(key) + end + + rescue Exception => e + @log.debug e.message + serialparser.close + @running = false + @parser.kill + @parser = nil + + end + end @log.debug "Dispatcher#start has been called starting the system up.." @running = true return true @@ -319,11 +358,12 @@ @running = true return true end end rescue Exception => e - `killall node` + @parser.kill + @parser = nil raise e end @log.debug "The SerialParser system is already running.. ignoring.." return false @@ -344,28 +384,27 @@ begin unless Ardtweeno.options[:test] unless @running == false - @log.debug "Dispatcher#stop has been called shutting system down.." - - Process.kill("SIGTERM", @parser) - Process.wait + @parser.kill @parser = nil @running = false + @log.debug "Dispatcher#stop has been called shutting system down.." + return true end else unless @running == false @running = false return true end end rescue Exception => e - `killall node` + @parser.kill @parser = nil raise e end @log.debug "SerialParser system is inactive.. ignoring.." @@ -391,11 +430,62 @@ Signal.trap("SIGTERM") { exit } `#{cmd}` end end + + ## + # Ardtweeno::Dispatcher#status? returns the system status of the Ardtweeno Gateway host + # + # * *Args* : + # - ++ -> + # * *Returns* : + # - Hash theResponse containing: bool running, String cpuload, String memload + # * *Raises* : + # + def status?() + @log.debug "Ardtweeno::Dispatcher#status? executing" + begin + unless Ardtweeno.options[:test] ||= false + # Get CPU + maxLoad = calculateCPUCores() + + # Get Avgload + currentLoadPercentage = calculateAvgLoad(maxLoad) + + # Get MEM Usage + usedMem, totalMem = calculateMemLoad() + + + thecpuload = '%.2f' % currentLoadPercentage + thememload = '%.2f' % ((usedMem / totalMem.to_f) * 100) + + theResponse = {:running=>@running, + :cpuload=>thecpuload, + :memload=>thememload} + + @log.debug theResponse.inspect + + return theResponse + + else # When in testing mode, return blank data + theResponse = {:running=>@running, + :cpuload=>0.0, + :memload=>0.0} + + @log.debug theResponse.inspect + + return theResponse + end + + rescue Exception => e + + end + end + + ## # Ardtweeno::Dispatcher#running? checks to see if the SerialParser is running # # * *Args* : # - ++ -> @@ -432,15 +522,64 @@ end end ## - # Ardtweeno::Dispatcher#config returns the configuration as read in from the DB + # Ardtweeno::Dispatcher#getPostsURI returns the front page news URI ~/.ardtweeno/conf.yaml # # * *Args* : # - ++ -> # * *Returns* : + # - String which makes up the news post URI ie "/randomhash/create/post" + # * *Raises* : + # + def getPostsURI() + return @confdata["newsURI"] + end + + + + ## + # Ardtweeno::Dispatcher#getPosts returns the front page news posts loaded from ~/.ardtweeno/posts.yaml + # + # * *Args* : + # - ++ -> + # * *Returns* : + # - Array of Hash containing post data + # * *Raises* : + # + def getPosts() + unless @posts.nil? or @posts.empty? + return @posts["posts"] + else + return Array.new + end + end + + + ## + # Ardtweeno::Dispatcher#savePosts saves a post to ~/.ardtweeno/posts.yaml + # + # * *Args* : + # - ++ -> + # * *Returns* : + # - + # * *Raises* : + # + def savePosts(newPosts) + @posts["posts"] = newPosts + Ardtweeno::ConfigReader.save(@posts, Ardtweeno::POSTPATH) + end + + + ## + # Ardtweeno::Dispatcher#config returns the configuration as read in from the confg.yaml configuration + # file + # + # * *Args* : + # - ++ -> + # * *Returns* : # - @confdata # * *Raises* : # def config() return @confdata @@ -454,19 +593,19 @@ # - ++ -> # * *Returns* : # - # * *Raises* : # - private - def bootstrap() + def bootstrap # Read in the configuration files begin @log.debug "Reading in the configuration files" @confdata = Ardtweeno::ConfigReader.load(Ardtweeno::DBPATH) @nodedata = Ardtweeno::ConfigReader.load(Ardtweeno::NODEPATH) + @posts = Ardtweeno::ConfigReader.load(Ardtweeno::POSTPATH) rescue Exception => e @log.fatal e.message @log.fatal e.backtrace raise e @@ -498,11 +637,11 @@ @nodeManager = Ardtweeno::NodeManager.new(nmoptions) rescue Exception => e @log.debug e.message @log.debug e.backtrace raise e - end + end # Create the MongoDB connector instance begin @log.debug @confdata["db"]["dbHost"] @@ -530,9 +669,92 @@ raise e end end # End of the bootstrap() + + + def calculateMemLoad() + begin + memhash = Hash.new + meminfo = File.read('/proc/meminfo') + meminfo.each_line do |i| + key, val = i.split(':') + if val.include?('kB') then val = val.gsub(/\s+kB/, ''); end + memhash["#{key}"] = val.strip + end + + totalMem = memhash["MemTotal"].to_i + freeMem = memhash["MemFree"].to_i + memhash["Buffers"].to_i + memhash["Cached"].to_i + usedMem = totalMem - freeMem + + @log.debug "Total Memory: #{totalMem} (100%)" + @log.debug "Used Memory: #{usedMem} (#{'%.2f' % ((usedMem / totalMem.to_f) * 100)}%)" + @log.debug "Free Memory: #{freeMem} (#{'%.2f' % ((freeMem / totalMem.to_f) * 100)}%)" + + return usedMem, totalMem + + rescue Exception => e + @log.debug "Some issue accessing /proc/meminfo" + usedMem, totalMem = 0, 0 + + return usedMem, totalMem + end + end + def calculateAvgLoad(maxLoad) + begin + loadavg = File.read('/proc/loadavg') + loads = loadavg.scan(/\d+.\d+/) + onemin = loads[0] + fivemin = loads[1] + fifteenmin = loads[2] + + @log.debug "LoadAvg are as follows: 1min #{onemin}, 5min #{fivemin}, 15min #{fifteenmin}" + + loadval = (onemin.to_f / maxLoad) + currentLoadPercentage = loadval * 100 + + @log.debug "Currently running at #{'%.2f' % currentLoadPercentage}% of max load" + + return currentLoadPercentage + + rescue Exception => e + @log.debug "Some issue accessing /proc/loadavg" + onemin, fivemin, fifteenmin = 0, 0, 0 + + loadval = (onemin.to_f / maxLoad) + currentLoadPercentage = loadval * 100 + + return currentLoadPercentage + end + end + + + def calculateCPUCores() + begin # Checking for multi-core CPU + cpuinfo = File.read('/proc/cpuinfo') + coreinfo = cpuinfo.scan(/cpu cores\s+:\s+\d+/) + + tempVal = coreinfo[0] + numOfCores = tempVal.scan(/\d+/)[0].to_i + numOfThreadsPerCore = coreinfo.size / numOfCores + maxLoad = (numOfThreadsPerCore * numOfCores).to_f + + @log.debug "Found #{numOfCores} cores with #{numOfThreadsPerCore} threads per core" + @log.debug "Max desirable cpu load: #{maxLoad}" + + return maxLoad + + rescue Exception => e + @log.debug "Unable to find cpu core info in /proc/cpuinfo, assuming system has a single core" + maxLoad = 1.0 + + return maxLoad + end + end + + + private :bootstrap, :calculateMemLoad, :calculateAvgLoad, :calculateCPUCores end end