ardtweeno 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG +60 -0
- data/Gemfile +0 -1
- data/Gemfile.lock +35 -33
- data/INSTALL +12 -8
- data/README.md +17 -17
- data/Rakefile +20 -8
- data/lib/ardtweeno/api.rb +409 -9
- data/lib/ardtweeno/dispatcher.rb +177 -128
- data/lib/ardtweeno/nodemanager.rb +33 -14
- data/lib/ardtweeno/restapi.rb +77 -26
- data/lib/ardtweeno/ringbuffer.rb +115 -0
- data/lib/ardtweeno.rb +2 -2
- data/public/favicon.ico +0 -0
- data/public/main.css +80 -14
- data/public/main.js +8 -1
- data/test/api_test.rb +60 -7
- data/test/debug/run_mock +1 -1
- data/test/debug/run_packet_push +4 -4
- data/test/debug/run_post_watch +4 -4
- data/test/debug/tty0tty-1.1/pts/tty0tty +0 -0
- data/test/dispatcher_test.rb +11 -8
- data/test/node_test.rb +7 -3
- data/test/nodemanager_test.rb +9 -5
- data/test/packet_test.rb +7 -3
- data/test/parser_test.rb +1 -3
- data/test/rest_api_test.rb +8 -0
- data/test/ringbuffer_test.rb +64 -0
- data/views/home.erb +2 -1
- data/views/index.erb +14 -10
- data/views/punchcard.erb +55 -0
- data/views/status.erb +48 -135
- data/views/topology.erb +12 -0
- metadata +7 -34
- data/public/bootstrap.min.js +0 -6
- data/public/highcharts.js +0 -270
- data/public/jquery-1.8.2.min.js +0 -2
- data/public/jquery-2.0.1.min.js +0 -6
data/lib/ardtweeno/api.rb
CHANGED
@@ -1,12 +1,11 @@
|
|
1
1
|
####################################################################################################
|
2
|
-
# @author David Kirwan
|
2
|
+
# @author David Kirwan https://github.com/davidkirwan/ardtweeno
|
3
3
|
# @description API class for the Ardtweeno system
|
4
4
|
#
|
5
|
-
# @date
|
5
|
+
# @date 2013-08-05
|
6
6
|
####################################################################################################
|
7
7
|
|
8
8
|
# Imports
|
9
|
-
require 'rubygems'
|
10
9
|
require 'logger'
|
11
10
|
require 'yaml'
|
12
11
|
require 'json'
|
@@ -20,7 +19,6 @@ module Ardtweeno
|
|
20
19
|
attr_accessor :log
|
21
20
|
|
22
21
|
|
23
|
-
|
24
22
|
##
|
25
23
|
# Ardtweeno::API#retrievezones method to filter node zone according to REST API request
|
26
24
|
#
|
@@ -203,7 +201,7 @@ module Ardtweeno
|
|
203
201
|
@log.debug "version not found, returning empty array"
|
204
202
|
end
|
205
203
|
|
206
|
-
return containerArray
|
204
|
+
return containerArray
|
207
205
|
end
|
208
206
|
|
209
207
|
|
@@ -533,10 +531,412 @@ module Ardtweeno
|
|
533
531
|
|
534
532
|
|
535
533
|
|
534
|
+
##
|
535
|
+
# Ardtweeno::API#parseTopology method to construct the topology Hash used in API#buildTopology
|
536
|
+
#
|
537
|
+
# * *Args* :
|
538
|
+
# - ++ -> Hash zones containing raw data from API#retrievezones,
|
539
|
+
# Hash nodes containing raw data from API#retrievenodes
|
540
|
+
# * *Returns* :
|
541
|
+
# -
|
542
|
+
# * *Raises* :
|
543
|
+
#
|
544
|
+
def parseTopology(zones, nodes)
|
545
|
+
@log = Ardtweeno.options[:log] ||= Logger.new(STDOUT)
|
546
|
+
@log.level = Ardtweeno.options[:level] ||= Logger::DEBUG
|
547
|
+
|
548
|
+
zonelist = Array.new
|
549
|
+
|
550
|
+
begin
|
551
|
+
|
552
|
+
zones[:zones].each do |i|
|
553
|
+
zonename = i[:zonename]
|
554
|
+
nodelist = Array.new
|
555
|
+
|
556
|
+
i[:nodes].each do |j|
|
557
|
+
nodename = j
|
558
|
+
sensorlist = Array.new
|
559
|
+
|
560
|
+
nodes[:nodes].each do |k|
|
561
|
+
if nodename == k[:name]
|
562
|
+
sensorlist = k[:sensors]
|
563
|
+
end
|
564
|
+
end
|
565
|
+
|
566
|
+
nodelist << {:name=> nodename, :sensorlist=> sensorlist}
|
567
|
+
end
|
568
|
+
|
569
|
+
zonelist << {:zonename=>zonename, :nodes=>nodelist}
|
570
|
+
end
|
571
|
+
|
572
|
+
response = zonelist
|
573
|
+
|
574
|
+
rescue Exception => e
|
575
|
+
@log.debug e
|
576
|
+
return e
|
577
|
+
end
|
578
|
+
|
579
|
+
@log.debug response.inspect
|
580
|
+
return response
|
581
|
+
|
582
|
+
end
|
583
|
+
|
536
584
|
|
537
|
-
|
538
|
-
|
585
|
+
|
586
|
+
##
|
587
|
+
# Ardtweeno::API#buildTopology method for constructing the topology graph
|
588
|
+
#
|
589
|
+
# * *Args* :
|
590
|
+
# - ++ -> Hash containing structured topology data
|
591
|
+
# * *Returns* :
|
592
|
+
# - String containing the Raphael.js code to generate the graph
|
593
|
+
# * *Raises* :
|
594
|
+
#
|
595
|
+
#
|
596
|
+
def buildTopology(topology)
|
597
|
+
@log = Ardtweeno.options[:log] ||= Logger.new(STDOUT)
|
598
|
+
@log.level = Ardtweeno.options[:level] ||= Logger::DEBUG
|
599
|
+
|
600
|
+
@log.debug "Number of Zones: #{topology.count.to_s}"
|
601
|
+
response = "" # Will hold our response
|
602
|
+
offset = 0 # Offset
|
603
|
+
totalsensorcount = countSensors(topology)
|
604
|
+
@log.debug "Total Sensor Count: " + totalsensorcount.to_s
|
605
|
+
|
606
|
+
# Canvas height
|
607
|
+
defaultheight = 700
|
608
|
+
height = 100 + (totalsensorcount * 100)
|
609
|
+
if height <= defaultheight
|
610
|
+
height = defaultheight
|
611
|
+
@log.debug "Height less than defaultheight, setting canvas height to 700"
|
612
|
+
end
|
613
|
+
@log.debug "Canvas height: " + height.to_s
|
614
|
+
|
615
|
+
# Set up the Canvas
|
616
|
+
response += "var thepaper = new Raphael(document.getElementById('topology-canvas'), " +
|
617
|
+
"500, #{height});\n"
|
618
|
+
|
619
|
+
# Draw the graph
|
620
|
+
topology.each_with_index do |i, index|
|
621
|
+
|
622
|
+
# Initial hookup line
|
623
|
+
response += "var hookup1 = thepaper.path('M 50 #{75 + offset} l 50 0');\n"
|
624
|
+
|
625
|
+
# Print the Zone name
|
626
|
+
response += "var zonetitle = thepaper.text(50, #{20+ offset}, '#{i[:zonename]}').attr({'font-size':20});"
|
627
|
+
|
628
|
+
# Print the sensors
|
629
|
+
i[:nodes].each_with_index do |j, jndex|
|
630
|
+
|
631
|
+
# Print the node
|
632
|
+
response += "var node = thepaper.path('M 100 #{100 + offset} " +
|
633
|
+
"l 0 -50 l 50 0 l 0 50 l -50 0').attr(" +
|
634
|
+
"{fill: 'red', 'href':'/graph/v1/punchcard/#{j[:name]}'});\n"
|
635
|
+
|
636
|
+
# Print the node name
|
637
|
+
response += "var nodetitle = thepaper.text(125, #{40 + offset}, '#{j[:name]}');"
|
638
|
+
|
639
|
+
|
640
|
+
# Print the link to next node
|
641
|
+
if i[:nodes].count > 1
|
642
|
+
unless (jndex + 1) == i.count
|
643
|
+
response += "var nextnode1 = thepaper.path('M 75 #{75 + offset} l 0 " +
|
644
|
+
"#{(j[:sensorlist].count * 100) + 75} l 25 0');"
|
645
|
+
end
|
646
|
+
end
|
647
|
+
|
648
|
+
# Print the sensors
|
649
|
+
j[:sensorlist].each_with_index do |k, kndex|
|
650
|
+
# Sensor 1 in each node is drawn slightly differently
|
651
|
+
if kndex == 0
|
652
|
+
response += "var theline = thepaper.path('M 150 #{75 + offset} l 100 0');\n"
|
653
|
+
response += "var thecircle = thepaper.circle(270, #{ 75 + offset}" +
|
654
|
+
", 20).attr({fill:'green'});\n"
|
655
|
+
|
656
|
+
# Print sensortitle
|
657
|
+
response += "var sensor1Title = thepaper.text(350, #{75 + offset}, '#{k}');"
|
658
|
+
|
659
|
+
offset += 75
|
660
|
+
else
|
661
|
+
# Sensors beyond first
|
662
|
+
response += "var theline = thepaper.path('M 200 #{offset} l 0 75 l 50 0');"
|
663
|
+
response += "var thecircle = thepaper.circle(270, #{ 75 + offset}, 20).attr({fill:'green'});\n"
|
664
|
+
|
665
|
+
# Print sensortitle
|
666
|
+
response += "var sensor1Title = thepaper.text(350, #{75 + offset}, '#{k}');"
|
667
|
+
|
668
|
+
offset += 75
|
669
|
+
end
|
670
|
+
|
671
|
+
end
|
672
|
+
offset += 100
|
673
|
+
end
|
674
|
+
|
675
|
+
end
|
676
|
+
|
677
|
+
return response
|
678
|
+
end
|
679
|
+
|
680
|
+
|
681
|
+
##
|
682
|
+
# Ardtweeno::API#countSensors private method for counting the number of sensors in the toplogy
|
683
|
+
#
|
684
|
+
# * *Args* :
|
685
|
+
# - ++ -> Hash containing structured topology data
|
686
|
+
# * *Returns* :
|
687
|
+
# - Fixnum count of the number of sensors in the topology
|
688
|
+
# * *Raises* :
|
689
|
+
#
|
690
|
+
#
|
691
|
+
def countSensors(topology)
|
692
|
+
count = 0
|
693
|
+
topology.each do |i|
|
694
|
+
unless i[:nodes].nil?
|
695
|
+
i[:nodes].each do |j|
|
696
|
+
unless j[:sensorlist].nil?
|
697
|
+
count += j[:sensorlist].count
|
698
|
+
end
|
699
|
+
end
|
700
|
+
end
|
701
|
+
end
|
702
|
+
return count
|
703
|
+
end
|
704
|
+
|
705
|
+
|
706
|
+
|
707
|
+
##
|
708
|
+
# Ardtweeno::API#buildPunchcard generate the data used in the Punchcard graph
|
709
|
+
#
|
710
|
+
# * *Args* :
|
711
|
+
# - ++ -> Array of Ardtweeno::Node, Hash params
|
712
|
+
# * *Returns* :
|
713
|
+
# - Array Fixnum, 168 data hourly packet total values for last week,
|
714
|
+
# Array String previous 7 day names
|
715
|
+
# * *Raises* :
|
716
|
+
#
|
717
|
+
#
|
718
|
+
def buildPunchcard(nodeList, params)
|
719
|
+
@log = Ardtweeno.options[:log] ||= Logger.new(STDOUT)
|
720
|
+
@log.level = Ardtweeno.options[:level] ||= Logger::WARN
|
721
|
+
|
722
|
+
theParams = Hash.new
|
723
|
+
theParams[:node] = params[:node]
|
724
|
+
|
725
|
+
data = Array.new
|
726
|
+
days = Array.new
|
727
|
+
|
728
|
+
today = DateTime.now
|
729
|
+
|
730
|
+
theStart = today - 6
|
539
731
|
|
732
|
+
theStartDay = "%02d" % theStart.day
|
733
|
+
theStartMonth = "%02d" % theStart.month
|
734
|
+
theStartYear = theStart.year.to_s
|
735
|
+
|
736
|
+
theEndDay = "%02d" % today.day
|
737
|
+
theEndMonth = "%02d" % today.month
|
738
|
+
theEndYear = today.year.to_s
|
739
|
+
|
740
|
+
startRange = theStartYear + "-" + theStartMonth + "-" + theStartDay
|
741
|
+
endRange = theEndYear + "-" + theEndMonth + "-" + theEndDay
|
742
|
+
|
743
|
+
@log.debug "From #{startRange} to #{endRange}"
|
744
|
+
|
745
|
+
|
746
|
+
(theStart..today).each do |i|
|
747
|
+
theDay = theStart.strftime('%a')
|
748
|
+
days << theDay
|
749
|
+
@log.debug theDay
|
750
|
+
|
751
|
+
(0..23).each do |j|
|
752
|
+
theDate = theStart.year.to_s + "-" + "%02d" % theStart.month + "-" + "%02d" % i.day
|
753
|
+
theHour = "%02d" % j
|
754
|
+
|
755
|
+
theParams = {:hour=>theHour, :date=>theDate}
|
756
|
+
|
757
|
+
nodes = Ardtweeno::API.retrievepackets(nodeList, theParams)
|
758
|
+
|
759
|
+
data << nodes[:found].to_i
|
760
|
+
end
|
761
|
+
|
762
|
+
theStart += 1
|
763
|
+
end
|
764
|
+
|
765
|
+
@log.debug days.inspect
|
766
|
+
|
767
|
+
return data, days.reverse, "#{startRange} to #{endRange}"
|
768
|
+
end
|
769
|
+
|
770
|
+
|
771
|
+
def status
|
772
|
+
@log = Ardtweeno.options[:log] ||= Logger.new(STDOUT)
|
773
|
+
@log.level = Ardtweeno.options[:level] ||= Logger::DEBUG
|
774
|
+
# Get CPU
|
775
|
+
maxLoad = calculateCPUCores()
|
776
|
+
|
777
|
+
# Get Avgload
|
778
|
+
currentLoadPercentage = calculateAvgLoad(maxLoad)
|
779
|
+
|
780
|
+
# Get MEM Usage
|
781
|
+
usedMem, totalMem = calculateMemLoad()
|
782
|
+
|
783
|
+
|
784
|
+
thecpuload = '%.2f' % currentLoadPercentage
|
785
|
+
thememload = '%.2f' % ((usedMem / totalMem.to_f) * 100)
|
786
|
+
|
787
|
+
theResponse = {:cpuload=>thecpuload,
|
788
|
+
:memload=>thememload}
|
789
|
+
|
790
|
+
@log.debug theResponse.inspect
|
791
|
+
|
792
|
+
return theResponse
|
793
|
+
end
|
794
|
+
|
795
|
+
|
796
|
+
##
|
797
|
+
# Ardtweeno::API#calculateMemLoad calculate the Memory load on the system
|
798
|
+
#
|
799
|
+
# * *Args* :
|
800
|
+
# - ++ ->
|
801
|
+
# * *Returns* :
|
802
|
+
# - Fixednum usedmem, Fixedmem totalmem
|
803
|
+
# * *Raises* :
|
804
|
+
#
|
805
|
+
#
|
806
|
+
def calculateMemLoad()
|
807
|
+
begin
|
808
|
+
memhash = Hash.new
|
809
|
+
meminfo = File.read('/proc/meminfo')
|
810
|
+
meminfo.each_line do |i|
|
811
|
+
key, val = i.split(':')
|
812
|
+
if val.include?('kB') then val = val.gsub(/\s+kB/, ''); end
|
813
|
+
memhash["#{key}"] = val.strip
|
814
|
+
end
|
815
|
+
|
816
|
+
totalMem = memhash["MemTotal"].to_i
|
817
|
+
freeMem = memhash["MemFree"].to_i + memhash["Buffers"].to_i + memhash["Cached"].to_i
|
818
|
+
usedMem = totalMem - freeMem
|
819
|
+
|
820
|
+
@log.debug "Total Memory: #{totalMem} (100%)"
|
821
|
+
@log.debug "Used Memory: #{usedMem} (#{'%.2f' % ((usedMem / totalMem.to_f) * 100)}%)"
|
822
|
+
@log.debug "Free Memory: #{freeMem} (#{'%.2f' % ((freeMem / totalMem.to_f) * 100)}%)"
|
823
|
+
|
824
|
+
return usedMem, totalMem
|
825
|
+
|
826
|
+
rescue Exception => e
|
827
|
+
@log.debug "Some issue accessing /proc/meminfo"
|
828
|
+
usedMem, totalMem = 0, 0
|
829
|
+
|
830
|
+
return usedMem, totalMem
|
831
|
+
end
|
832
|
+
end
|
833
|
+
|
834
|
+
|
835
|
+
##
|
836
|
+
# Ardtweeno::API#calculateAvgLoad calculate the average CPU load on the system
|
837
|
+
#
|
838
|
+
# * *Args* :
|
839
|
+
# - ++ -> Float maxload
|
840
|
+
# * *Returns* :
|
841
|
+
# - Float loadpercentage
|
842
|
+
# * *Raises* :
|
843
|
+
#
|
844
|
+
#
|
845
|
+
def calculateAvgLoad(maxLoad)
|
846
|
+
begin
|
847
|
+
loadavg = File.read('/proc/loadavg')
|
848
|
+
loads = loadavg.scan(/\d+.\d+/)
|
849
|
+
onemin = loads[0]
|
850
|
+
fivemin = loads[1]
|
851
|
+
fifteenmin = loads[2]
|
852
|
+
|
853
|
+
@log.debug "LoadAvg are as follows: 1min #{onemin}, 5min #{fivemin}, 15min #{fifteenmin}"
|
854
|
+
|
855
|
+
loadval = (onemin.to_f / maxLoad)
|
856
|
+
currentLoadPercentage = loadval * 100
|
857
|
+
|
858
|
+
@log.debug "Currently running at #{'%.2f' % currentLoadPercentage}% of max load"
|
859
|
+
|
860
|
+
return currentLoadPercentage
|
861
|
+
|
862
|
+
rescue Exception => e
|
863
|
+
@log.debug "Some issue accessing /proc/loadavg"
|
864
|
+
onemin, fivemin, fifteenmin = 0, 0, 0
|
865
|
+
|
866
|
+
loadval = (onemin.to_f / maxLoad)
|
867
|
+
currentLoadPercentage = loadval * 100
|
868
|
+
|
869
|
+
return currentLoadPercentage
|
870
|
+
end
|
871
|
+
end
|
872
|
+
|
873
|
+
|
874
|
+
##
|
875
|
+
# Ardtweeno::API#calculateCPUCores calculate number of CPU cores on the system and returns the
|
876
|
+
# maximum desirable load
|
877
|
+
#
|
878
|
+
# * *Args* :
|
879
|
+
# - ++ ->
|
880
|
+
# * *Returns* :
|
881
|
+
# - Float maxload
|
882
|
+
# * *Raises* :
|
883
|
+
#
|
884
|
+
#
|
885
|
+
def calculateCPUCores()
|
886
|
+
begin # Checking for multi-core CPU
|
887
|
+
cpuinfo = File.read('/proc/cpuinfo')
|
888
|
+
coreinfo = cpuinfo.scan(/cpu cores\s+:\s+\d+/)
|
889
|
+
|
890
|
+
tempVal = coreinfo[0]
|
891
|
+
numOfCores = tempVal.scan(/\d+/)[0].to_i
|
892
|
+
numOfThreadsPerCore = coreinfo.size / numOfCores
|
893
|
+
maxLoad = (numOfThreadsPerCore * numOfCores).to_f
|
894
|
+
|
895
|
+
@log.debug "Found #{numOfCores} cores with #{numOfThreadsPerCore} threads per core"
|
896
|
+
@log.debug "Max desirable cpu load: #{maxLoad}"
|
897
|
+
|
898
|
+
return maxLoad
|
899
|
+
|
900
|
+
rescue Exception => e
|
901
|
+
@log.debug "Unable to find cpu core info in /proc/cpuinfo, assuming system has a single core"
|
902
|
+
maxLoad = 1.0
|
903
|
+
|
904
|
+
return maxLoad
|
905
|
+
end
|
906
|
+
end
|
907
|
+
|
908
|
+
|
909
|
+
##
|
910
|
+
# Ardtweeno::API#diskUsage parse the disk usage statistics outputted by the linux utility df
|
911
|
+
#
|
912
|
+
# * *Args* :
|
913
|
+
# - ++ ->
|
914
|
+
# * *Returns* :
|
915
|
+
# - Array of Hash {String, String, String, String, String, String}
|
916
|
+
# * *Raises* :
|
917
|
+
#
|
918
|
+
#
|
919
|
+
def diskUsage
|
920
|
+
diskusage = Array.new
|
540
921
|
|
541
|
-
|
542
|
-
|
922
|
+
fileinfo = `df -h`
|
923
|
+
|
924
|
+
lines = fileinfo.split("\n")
|
925
|
+
lines.delete_at(0)
|
926
|
+
|
927
|
+
lines.each do |i|
|
928
|
+
i.gsub!(/\s+/, " ")
|
929
|
+
device, size, used, avail, use, mount = i.split(" ")
|
930
|
+
diskusage << {:device=>device, :size=>size, :used=>used, :avail=>avail, :use=>use, :mount=>mount}
|
931
|
+
end
|
932
|
+
|
933
|
+
return diskusage
|
934
|
+
end
|
935
|
+
|
936
|
+
|
937
|
+
|
938
|
+
private :countSensors, :calculateMemLoad, :calculateAvgLoad, :calculateCPUCores
|
939
|
+
|
940
|
+
end
|
941
|
+
end # End of API class
|
942
|
+
end # End of Ardtweeno Module
|