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.
data/lib/ardtweeno/api.rb CHANGED
@@ -1,12 +1,11 @@
1
1
  ####################################################################################################
2
- # @author David Kirwan <davidkirwanirl@gmail.com>
2
+ # @author David Kirwan https://github.com/davidkirwan/ardtweeno
3
3
  # @description API class for the Ardtweeno system
4
4
  #
5
- # @date 21-02-2013
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
- end
538
- end # End of API class
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
- # End of Ardtweeno Module
542
- end
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