wackamole 0.0.1

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.
Files changed (146) hide show
  1. data/.bnsignore +16 -0
  2. data/.gitignore +2 -0
  3. data/History.txt +4 -0
  4. data/README.rdoc +88 -0
  5. data/Rakefile +42 -0
  6. data/aa.rb +25 -0
  7. data/aaa.txt +13 -0
  8. data/bin/wackamole +77 -0
  9. data/lib/app.rb +63 -0
  10. data/lib/controllers/dashboard.rb +30 -0
  11. data/lib/controllers/features.rb +41 -0
  12. data/lib/controllers/logs.rb +49 -0
  13. data/lib/controllers/mission.rb +27 -0
  14. data/lib/controllers/users.rb +41 -0
  15. data/lib/helpers/dashboard_helper.rb +32 -0
  16. data/lib/helpers/features_helper.rb +13 -0
  17. data/lib/helpers/flash_helper.rb +16 -0
  18. data/lib/helpers/logs_helper.rb +116 -0
  19. data/lib/helpers/main_helper.rb +182 -0
  20. data/lib/helpers/mission_helper.rb +59 -0
  21. data/lib/helpers/rails_helper.rb +7 -0
  22. data/lib/wackamole.rb +47 -0
  23. data/lib/wackamole/core_ext/date_time.rb +8 -0
  24. data/lib/wackamole/core_ext/time.rb +9 -0
  25. data/lib/wackamole/models/control.rb +132 -0
  26. data/lib/wackamole/models/feature.rb +74 -0
  27. data/lib/wackamole/models/log.rb +45 -0
  28. data/lib/wackamole/models/mission.rb +164 -0
  29. data/lib/wackamole/models/mole_info.rb +94 -0
  30. data/lib/wackamole/models/search_filter.rb +192 -0
  31. data/lib/wackamole/models/user.rb +46 -0
  32. data/public/favicon.ico +0 -0
  33. data/public/images/.DS_Store +0 -0
  34. data/public/images/browsers/.DS_Store +0 -0
  35. data/public/images/browsers/MSIE 6.png +0 -0
  36. data/public/images/browsers/MSIE 8.0 +0 -0
  37. data/public/images/browsers/MSIE 8.0.png +0 -0
  38. data/public/images/browsers/chrome.png +0 -0
  39. data/public/images/browsers/firefox.png +0 -0
  40. data/public/images/browsers/ie 6.0.png +0 -0
  41. data/public/images/browsers/ie6.jpg +0 -0
  42. data/public/images/browsers/ie7.jpg +0 -0
  43. data/public/images/browsers/ie7.png +0 -0
  44. data/public/images/browsers/msie 6.0.png +0 -0
  45. data/public/images/browsers/msie 7.0.png +0 -0
  46. data/public/images/browsers/opera.jpg +0 -0
  47. data/public/images/browsers/opera.png +0 -0
  48. data/public/images/browsers/safari.jpg +0 -0
  49. data/public/images/browsers/safari.png +0 -0
  50. data/public/images/browsers/unknown_browser.png +0 -0
  51. data/public/images/close.png +0 -0
  52. data/public/images/error.png +0 -0
  53. data/public/images/fade.png +0 -0
  54. data/public/images/fault_big.png +0 -0
  55. data/public/images/fault_small.png +0 -0
  56. data/public/images/feature.png +0 -0
  57. data/public/images/h300.png +0 -0
  58. data/public/images/hori_large.png +0 -0
  59. data/public/images/info_big.png +0 -0
  60. data/public/images/info_small.png +0 -0
  61. data/public/images/loading.gif +0 -0
  62. data/public/images/loading1.gif +0 -0
  63. data/public/images/mole_error.png +0 -0
  64. data/public/images/more.gif +0 -0
  65. data/public/images/next.png +0 -0
  66. data/public/images/perf_big.png +0 -0
  67. data/public/images/perf_small.png +0 -0
  68. data/public/images/powered_by.png +0 -0
  69. data/public/images/prev.png +0 -0
  70. data/public/images/row_fade.png +0 -0
  71. data/public/images/search.png +0 -0
  72. data/public/images/small_logo.png +0 -0
  73. data/public/images/spaceball.gif +0 -0
  74. data/public/images/tick.png +0 -0
  75. data/public/images/users.png +0 -0
  76. data/public/images/wackamole_logo.png +0 -0
  77. data/public/javascripts/.DS_Store +0 -0
  78. data/public/javascripts/application.js +28 -0
  79. data/public/javascripts/g.dot.min.js +7 -0
  80. data/public/javascripts/g.raphael.min.js +7 -0
  81. data/public/javascripts/jit.js +9052 -0
  82. data/public/javascripts/jit.min.js +1 -0
  83. data/public/javascripts/jquery-ui.js +188 -0
  84. data/public/javascripts/jquery.example.js +160 -0
  85. data/public/javascripts/jquery.js +19 -0
  86. data/public/javascripts/jquery.tools.min.js +38 -0
  87. data/public/javascripts/jquery_ba-url.js +9 -0
  88. data/public/javascripts/jquery_min.js +19 -0
  89. data/public/javascripts/jquery_ui_min.js +298 -0
  90. data/public/javascripts/raphael.min.js +7 -0
  91. data/public/stylesheets/tabs-slideshow.css +95 -0
  92. data/public/stylesheets/wackamole.css +684 -0
  93. data/spec/config/bogus_test.yml +3 -0
  94. data/spec/config/test.yml +3 -0
  95. data/spec/core_ext/date_time_spec.rb +8 -0
  96. data/spec/core_ext/time_spec.rb +12 -0
  97. data/spec/data/fixtures.rb +92 -0
  98. data/spec/models/control_spec.rb +98 -0
  99. data/spec/models/feature_spec.rb +52 -0
  100. data/spec/models/log_spec.rb +43 -0
  101. data/spec/models/mission_spec.rb +225 -0
  102. data/spec/models/moled_info_spec.rb +30 -0
  103. data/spec/models/search_filter_spec.rb +151 -0
  104. data/spec/models/user_spec.rb +44 -0
  105. data/spec/spec_helper.rb +14 -0
  106. data/spec/wackamole_spec.rb +20 -0
  107. data/tasks/bones.rake +20 -0
  108. data/tasks/fixtures.rake +13 -0
  109. data/tasks/gem.rake +201 -0
  110. data/tasks/git.rake +40 -0
  111. data/tasks/notes.rake +27 -0
  112. data/tasks/post_load.rake +32 -0
  113. data/tasks/rdoc.rake +54 -0
  114. data/tasks/rubyforge.rake +55 -0
  115. data/tasks/setup.rb +290 -0
  116. data/tasks/spec.rake +54 -0
  117. data/tasks/svn.rake +46 -0
  118. data/views/dashboard/_report.erb +118 -0
  119. data/views/dashboard/index.erb +4 -0
  120. data/views/dashboard/refresh_js.erb +3 -0
  121. data/views/features/_rows.erb +26 -0
  122. data/views/features/filter.js.erb +2 -0
  123. data/views/features/index.erb +24 -0
  124. data/views/features/index.js.erb +2 -0
  125. data/views/layout.erb +47 -0
  126. data/views/logs/_array.erb +12 -0
  127. data/views/logs/_hash.erb +14 -0
  128. data/views/logs/_rows.erb +65 -0
  129. data/views/logs/filter.js.erb +4 -0
  130. data/views/logs/index.erb +13 -0
  131. data/views/logs/index.js.erb +2 -0
  132. data/views/logs/show.erb +66 -0
  133. data/views/mission/_report.erb +49 -0
  134. data/views/mission/index.erb +7 -0
  135. data/views/mission/refresh_js.erb +3 -0
  136. data/views/shared/_filter.erb +63 -0
  137. data/views/shared/_flash.erb +3 -0
  138. data/views/shared/_search.erb +25 -0
  139. data/views/shared/_timestamp.erb +1 -0
  140. data/views/shared/_wait.erb +1 -0
  141. data/views/shared/flash.js.erb +10 -0
  142. data/views/users/_rows.erb +26 -0
  143. data/views/users/filter.js.erb +4 -0
  144. data/views/users/index.erb +24 -0
  145. data/views/users/index.js.erb +2 -0
  146. metadata +347 -0
@@ -0,0 +1,7 @@
1
+ # module RailsHelper
2
+ #
3
+ # helpers do
4
+ # include Wackamole::TagHelper
5
+ # include Wackamole::FormHelper
6
+ # end
7
+ # end
@@ -0,0 +1,47 @@
1
+ module Wackamole
2
+
3
+ # :stopdoc:
4
+ VERSION = '0.0.1' unless defined? Wackamole::VERSION
5
+ LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR unless defined? Wackamole::LIBPATH
6
+ PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR unless defined? Wackamole::PATH
7
+ # :startdoc:
8
+
9
+ # Returns the version string for the library.
10
+ def self.version
11
+ VERSION
12
+ end
13
+
14
+ # Returns the library path for the module. If any arguments are given,
15
+ # they will be joined to the end of the libray path using
16
+ # <tt>File.join</tt>.
17
+ def self.libpath( *args )
18
+ args.empty? ? LIBPATH : ::File.join(LIBPATH, args.flatten)
19
+ end
20
+
21
+ # Returns the lpath for the module. If any arguments are given,
22
+ # they will be joined to the end of the path using
23
+ # <tt>File.join</tt>.
24
+ def self.path( *args )
25
+ args.empty? ? PATH : ::File.join(PATH, args.flatten)
26
+ end
27
+
28
+ # Utility method used to require all files ending in .rb that lie in the
29
+ # directory below this file that has the same name as the filename passed
30
+ # in. Optionally, a specific _directory_ name can be passed in such that
31
+ # the _filename_ does not have to be equivalent to the directory.
32
+ def self.require_all_libs_relative_to( fname, dir = nil )
33
+ dir ||= ::File.basename(fname, '.*')
34
+ search_me = ::File.expand_path(::File.join(::File.dirname(fname), dir, '**', '*.rb'))
35
+ Dir.glob(search_me).sort.each {|rb| require rb}
36
+ end
37
+
38
+ # Utility to force class load
39
+ def self.load_all_libs_relative_to( fname, dir = nil )
40
+ dir ||= ::File.basename(fname, '.*')
41
+ search_me = ::File.expand_path(::File.join(::File.dirname(fname), dir, '**', '*.rb'))
42
+ Dir.glob(search_me).sort.each {|rb| load rb}
43
+ end
44
+
45
+ end
46
+
47
+ Wackamole.require_all_libs_relative_to(__FILE__)
@@ -0,0 +1,8 @@
1
+ class DateTime
2
+
3
+ # ---------------------------------------------------------------------------
4
+ # Convert a datetime to an id ie => 20100101
5
+ def to_date_id
6
+ self.strftime( "%Y%m%d").to_i
7
+ end
8
+ end
@@ -0,0 +1,9 @@
1
+ class Time
2
+ def to_date_id
3
+ self.strftime( "%Y%m%d").to_i
4
+ end
5
+
6
+ def to_time_id
7
+ "%02d%02d%02d" % [self.hour, self.min, self.sec]
8
+ end
9
+ end
@@ -0,0 +1,132 @@
1
+ require 'mongo'
2
+ require 'logger'
3
+
4
+ module Wackamole
5
+ class Control
6
+
7
+ # -------------------------------------------------------------------------
8
+ # Defines mole db identity
9
+ def self.molex() @molex || /mole_(.*)?_(.*)?_mdb/; end
10
+
11
+ # -------------------------------------------------------------------------
12
+ # Fetch a collection on a given database by name
13
+ def self.collection( cltn_name, db_name=nil, opts={:strict => true} )
14
+ # reset_db!( db_name ) if db_name
15
+ db( db_name, opts ).collection( cltn_name )
16
+ end
17
+
18
+ # -------------------------------------------------------------------------
19
+ # extract app_name + env from db_name
20
+ def self.extract_app_info( db_name )
21
+ raise "Invalid mole db specification #{db_name}" unless db_name =~ molex
22
+ db_name.match( molex ).captures
23
+ end
24
+
25
+ # -------------------------------------------------------------------------
26
+ # Switch db instance given db_name
27
+ # NOTE : This assumes mole db naming convention
28
+ # ie mole_{app_name in lower case}_{env}_mdb
29
+ def self.switch_mole_db!( app_name, env )
30
+ raise "You must specify an app name and environment" unless app_name and env
31
+ app = app_name.gsub( /\s/, '_' ).downcase
32
+ db_name = to_mole_db( app_name, env )
33
+ raise "Invalid mole database #{db_name}" unless mole_db?( db_name )
34
+ reset_db!( db_name )
35
+ @db
36
+ end
37
+
38
+ # -------------------------------------------------------------------------
39
+ # Inspect current connection databases and weed out mole_xxx databases
40
+ def self.mole_databases
41
+ connection.database_names.select do |db_name|
42
+ db_name if mole_db?( db_name )
43
+ end
44
+ end
45
+
46
+ # =========================================================================
47
+ private
48
+
49
+ # -----------------------------------------------------------------------
50
+ # Computes mole_db name from app and env
51
+ def self.to_mole_db( app_name, env )
52
+ "mole_%s_%s_mdb" % [app_name, env]
53
+ end
54
+
55
+ # -----------------------------------------------------------------------
56
+ # Reset connection. For testing only!
57
+ def self.reset!
58
+ @connection.close if @connection
59
+ @connection = nil
60
+ @config = nil
61
+ end
62
+
63
+ # -----------------------------------------------------------------------
64
+ # Checks if this is a mole database
65
+ def self.mole_db?( db_name )
66
+ return false unless db_name =~ molex
67
+ db = connection.db( db_name )
68
+ cltns = db.collection_names
69
+ return ((%w[users features logs] & cltns).size == 3)
70
+ end
71
+
72
+ # -----------------------------------------------------------------------
73
+ # Ensures we have the right db connection
74
+ def self.reset_db!( db_name )
75
+ return if @db and @db.name == db_name
76
+ @db = nil
77
+ db( db_name )
78
+ end
79
+
80
+ # -----------------------------------------------------------------------
81
+ # Initialize app by reading off mongo configuration parameters if necessary
82
+ # BOZO !!
83
+ # File.join( ENV['HOME'], %w[.wackamole wackamole.yml] )
84
+ #
85
+ def self.init_config( config_file, env )
86
+ begin
87
+ config = YAML.load_file( config_file )
88
+ @config = config[env]
89
+ raise "Invalid environment `#{env}" unless @config
90
+ raise "Unable to find host in - #{@config.inspect}" unless @config.has_key?('host')
91
+ raise "Unable to find port in - #{@config.inspect}" unless @config.has_key?('port')
92
+ rescue => boom
93
+ @config = nil
94
+ raise "Hoy! An error occur loading the config file `#{config_file} -- #{boom}"
95
+ end
96
+ @config
97
+ end
98
+
99
+ def self.config
100
+ raise "You must call init_config before using this object" unless @config
101
+ @config
102
+ end
103
+
104
+ # -----------------------------------------------------------------------
105
+ # Connects to mongo instance if necessary...
106
+ def self.connection( log=false )
107
+ logger = nil
108
+ if log
109
+ logger = Logger.new($stdout)
110
+ logger.level = Logger::DEBUG
111
+ end
112
+ @connection ||= Mongo::Connection.new( config['host'], config['port'], :logger => logger )
113
+ end
114
+
115
+ # -----------------------------------------------------------------------
116
+ # Fetch database instance
117
+ def self.db( db_name=nil, opts={:strict => true} )
118
+ # puts "#{db_name} -- #{@db.inspect}"
119
+ return @db if @db and !db_name
120
+ return @db if @db and @db.name == db_name
121
+ raise "No database specified" unless db_name
122
+ @db = connection.db( db_name, opts )
123
+ ensure_indexes
124
+ @db
125
+ end
126
+
127
+ # -----------------------------------------------------------------------
128
+ # Make sure the right indexes are set
129
+ def self.ensure_indexes
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,74 @@
1
+ module Wackamole
2
+ class Feature
3
+
4
+ def self.logs_cltn() Wackamole::Control.collection( 'logs' ) ; end
5
+ def self.features_cltn() Wackamole::Control.collection( 'features' ); end
6
+
7
+ # Pagination size
8
+ def self.default_page_size() @page_size ||= 20; end
9
+
10
+ # ---------------------------------------------------------------------------
11
+ # Find the app name and env for the features collection
12
+ # NOTE: Assumes 1 moled app per db...
13
+ def self.get_app_info
14
+ feature = features_cltn.find_one( {}, :fields => [:app, :env] )
15
+ raise "Unable to find a single feature in db `#{features_cltn.db.name}" unless feature
16
+ { :app_name => feature['app'], :stage => feature['env'] }
17
+ end
18
+
19
+ # ---------------------------------------------------------------------------
20
+ # Paginate top features
21
+ def self.paginate_tops( conds, page=1, page_size=default_page_size )
22
+ tops = logs_cltn.group( [:fid], conds, { :count => 0 }, 'function(obj,prev) { prev.count += 1}', true )
23
+
24
+ features = []
25
+ tops.sort{ |a,b| b['count'] <=> a['count'] }.each do |row|
26
+ features << { :fid => row['fid'], :total => row['count'].to_i }
27
+ end
28
+
29
+ WillPaginate::Collection.create( page, page_size, features.size ) do |pager|
30
+ offset = (page-1)*page_size
31
+ result = features[offset...(offset+page_size)]
32
+ result.each do |u|
33
+ feature = features_cltn.find_one( u[:fid] )
34
+ u[:name] = feature
35
+ end
36
+ pager.replace( result )
37
+ end
38
+ end
39
+
40
+ # ---------------------------------------------------------------------------
41
+ # Make sure indexes are setup for users
42
+ def self.ensure_indexes!
43
+ indexes = features_cltn.index_information
44
+ created_count = 0
45
+
46
+ [:ctx].each do |name|
47
+ unless indexes.has_key?( "#{name}_1" )
48
+ features_cltn.create_index( name )
49
+ created_count += 1
50
+ end
51
+ end
52
+ unless indexes.has_key?( 'app_1_env_1' )
53
+ features_cltn.create_index(
54
+ [
55
+ [:app, Mongo::ASCENDING],
56
+ [:env, Mongo::ASCENDING]
57
+ ]
58
+ )
59
+ created_count += 1
60
+ end
61
+ unless indexes.has_key?( 'ctl_1_act_1' )
62
+ features_cltn.create_index(
63
+ [
64
+ [:ctl, Mongo::ASCENDING],
65
+ [:act, Mongo::ASCENDING]
66
+ ],
67
+ true
68
+ )
69
+ created_count += 1
70
+ end
71
+ created_count
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,45 @@
1
+ require 'forwardable'
2
+
3
+ module Wackamole
4
+ class Log
5
+ extend ::SingleForwardable
6
+
7
+ def self.logs_cltn() Wackamole::Control.collection( 'logs' ); end
8
+
9
+ def_delegators :logs_cltn, :find, :find_one
10
+
11
+ # Pagination size
12
+ def self.default_page_size() @page_size ||= 20; end
13
+
14
+ # ---------------------------------------------------------------------------
15
+ # Fetch all logs matching the given condition
16
+ def self.paginate( conds, page=1, page_size=default_page_size )
17
+ matching = logs_cltn.find( conds )
18
+ WillPaginate::Collection.create( page, page_size, matching.count ) do |pager|
19
+ pager.replace( logs_cltn.find( conds,
20
+ :sort => [ ['did', 'desc'], ['tid', 'desc'] ],
21
+ :skip => (page-1)*page_size,
22
+ :limit => page_size ).to_a )
23
+ end
24
+ end
25
+
26
+ # ---------------------------------------------------------------------------
27
+ # Makes sure the correct indexes are set
28
+ def self.ensure_indexes!
29
+ indexes = logs_cltn.index_information
30
+ created_count = 0
31
+
32
+ [:fid, :uid, :did, :tid].each do |name|
33
+ unless indexes.has_key?( "#{name}_1" )
34
+ logs_cltn.create_index( name )
35
+ created_count += 1
36
+ end
37
+ end
38
+ unless indexes.has_key?( 'did_-1_tid_-1' )
39
+ logs_cltn.create_index( [ [:did, Mongo::DESCENDING], [:tid, Mongo::DESCENDING] ] )
40
+ created_count += 1
41
+ end
42
+ created_count
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,164 @@
1
+ require 'forwardable'
2
+
3
+ module Wackamole
4
+ class Mission
5
+ extend ::SingleForwardable
6
+
7
+ def self.rollups_cltn() @rollups ||= Wackamole::Control.collection( 'rollups', 'wackamole_mdb', :strict => false ); end
8
+
9
+ def_delegators :rollups_cltn, :find, :find_one
10
+
11
+ # -------------------------------------------------------------------------
12
+ # Allows user to clear out perf or fault state til the next tick...
13
+ def self.reset!( app, env, type )
14
+ rollups_cltn.update( { :app => app }, { '$set' => { "envs.#{env}.#{type}" => 0 } } )
15
+ end
16
+
17
+ # -------------------------------------------------------------------------
18
+ # Clean up rollups. Check if mole_db is still around
19
+ def self.clean_up!
20
+ databases = Wackamole::Control.mole_databases
21
+ con = Wackamole::Control.connection
22
+ rollups = rollups_cltn.find( {} )
23
+ delete_list = []
24
+ rollups.each do |rollup|
25
+ app = rollup['app']
26
+ envs_info = rollup[envs].keys
27
+ envs_info.each do |env|
28
+ db_name = Wackamole::Control.to_mole_db( app, env )
29
+ delete_list << env unless databases.include?( db_name )
30
+ end
31
+ # if app is no longer around blow away the rollup
32
+ if delete_list.size == envs_info.size
33
+ rollups_cltn.remove( { :_id => rollup['_id'] } )
34
+ else
35
+ delete_list.each do |env|
36
+ rollup[envs].delete( env )
37
+ end
38
+ rollups_cltn.save( rollup, :safe => true ) unless delete_list.empty?
39
+ end
40
+ end
41
+ end
42
+
43
+ # -------------------------------------------------------------------------
44
+ # Retrieve reports if any...
45
+ # BOZO !! Handle case where report is no longer valid - ie no mole db
46
+ def self.rollups( now, reset )
47
+ clean_up!
48
+ rollups = comb_applications( now, reset )
49
+ rollups.each_pair do |app_name, env_info|
50
+ env_info[envs].each_pair do |env, info|
51
+ info.each_pair do |mole_type, count|
52
+ rollup = rollups_cltn.find_one( { :app => app_name } )
53
+ type_name = to_type_name( mole_type )
54
+ if rollup
55
+ rollup_info = rollup[envs]
56
+ if rollup_info and rollup_info[env]
57
+ (rollup_info[env][type_name] and !reset) ? rollup_info[env][type_name] += count : rollup_info[env][type_name] = count
58
+ elsif rollup_info
59
+ rollup_info[env] = { type_name => count }
60
+ # else
61
+ # rollup_info[envs] = { env => { type_name => count } }
62
+ end
63
+ rollups_cltn.save( rollup, :safe => true )
64
+ else
65
+ row = { :app => app_name, envs => { env => { type_name => count } } }
66
+ rollups_cltn.insert( row, :safe => true )
67
+ end
68
+ end
69
+ end
70
+ end
71
+ find( {}, :sort => [ [:app, Mongo::ASCENDING], [:env, Mongo::ASCENDING] ] ).to_a
72
+ end
73
+
74
+ # =========================================================================
75
+ private
76
+
77
+ # -----------------------------------------------------------------------
78
+ # Map rackamole types to report types
79
+ def self.to_type_name( type )
80
+ case type
81
+ when Rackamole.perf
82
+ "perfs"
83
+ when Rackamole.fault
84
+ "faults"
85
+ when Rackamole.feature
86
+ "features"
87
+ else
88
+ raise "Invalid mole log type `#{type}"
89
+ end
90
+ end
91
+
92
+ # ---------------------------------------------------------------------------
93
+ # Check moled apps status - reports any perf/excep that occurred since the
94
+ # last check
95
+ def self.comb_applications( now, reset )
96
+ report = {}
97
+ Wackamole::Control.mole_databases.each do |db_name|
98
+ db = Wackamole::Control.db( db_name )
99
+
100
+ app_name, env = Wackamole::Control.extract_app_info( db_name )
101
+ totals = analyse_logs( db, now, reset )
102
+
103
+ if report[app_name]
104
+ report[app_name][envs][env] = totals
105
+ else
106
+ report[app_name] = { envs => { env => totals } }
107
+ end
108
+ end
109
+ report
110
+ end
111
+
112
+ # -------------------------------------------------------------------------
113
+ # Report on possible application issues
114
+ def self.amend_report( report, app_name, env, log )
115
+ type = log['typ']
116
+
117
+ if report[app_name]
118
+ env_info = report[app_name][envs]
119
+ if env_info[env]
120
+ env_info[env][type] ? env_info[env][type] += 1 : env_info[env][type] = 1
121
+ else
122
+ env_info[ env ] = { type => 1 }
123
+ end
124
+ else
125
+ report[app_name] = { envs => { env => { type => 1 } } }
126
+ end
127
+ end
128
+
129
+ # -------------------------------------------------------------------------
130
+ # envs key
131
+ def self.envs() @envs ||= 'envs'; end
132
+
133
+ # -------------------------------------------------------------------------
134
+ # computes counts for each mole types
135
+ def self.analyse_logs( db, now, reset )
136
+ check_types = [Rackamole.perf, Rackamole.fault, Rackamole.feature]
137
+ date_id = now.to_date_id.to_s
138
+ time_id = now.to_time_id
139
+
140
+ if reset
141
+ conds = {
142
+ :did => date_id,
143
+ :typ => { '$in' => check_types }
144
+ }
145
+ else
146
+ conds = {
147
+ :did => { '$gte' => date_id },
148
+ :tid => { '$gte' => time_id },
149
+ :typ => { '$in' => check_types }
150
+ }
151
+ end
152
+
153
+ logs = db['logs'].find( conds, :fields => ['typ', 'rti', 'fault', 'fid'] )
154
+ totals =
155
+ {
156
+ Rackamole.feature => 0,
157
+ Rackamole.perf => 0,
158
+ Rackamole.fault => 0
159
+ }
160
+ logs.each { |log| totals[log['typ']] += 1 }
161
+ totals
162
+ end
163
+ end
164
+ end