slashport 0.15.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. data/Rakefile +35 -0
  2. data/app/controllers/application.rb +2 -0
  3. data/app/controllers/cfg.rb +8 -0
  4. data/app/controllers/exceptions.rb +13 -0
  5. data/app/controllers/var.rb +27 -0
  6. data/app/controllers/vardoc.rb +7 -0
  7. data/app/helpers/cfg_helper.rb +5 -0
  8. data/app/helpers/global_helpers.rb +5 -0
  9. data/app/helpers/var_helper.rb +5 -0
  10. data/app/helpers/vardoc_helper.rb +5 -0
  11. data/app/models/base/attribute.rb +13 -0
  12. data/app/models/base/component.rb +206 -0
  13. data/app/models/base/exec.rb +40 -0
  14. data/app/models/base/registry.rb +4 -0
  15. data/app/models/base/tuple.rb +31 -0
  16. data/app/models/components/linuxhost.rb +125 -0
  17. data/app/models/components/linuxprocess.rb +55 -0
  18. data/app/models/components/mysql.rb +110 -0
  19. data/app/models/components/puppet.rb +20 -0
  20. data/app/views/cfg/index.html.erb +1 -0
  21. data/app/views/exceptions/not_acceptable.html.erb +63 -0
  22. data/app/views/exceptions/not_found.html.erb +47 -0
  23. data/app/views/layout/application.html.erb +12 -0
  24. data/app/views/var/index.json.erb +1 -0
  25. data/app/views/var/index.pp.erb +10 -0
  26. data/app/views/var/index.text.erb +18 -0
  27. data/app/views/vardoc/index.html.erb +1 -0
  28. data/autotest/discover.rb +1 -0
  29. data/autotest/merb.rb +149 -0
  30. data/autotest/merb_rspec.rb +165 -0
  31. data/bin/slashport +130 -0
  32. data/bin/slashportfetch +103 -0
  33. data/config/environments/development.rb +15 -0
  34. data/config/environments/production.rb +10 -0
  35. data/config/environments/rake.rb +11 -0
  36. data/config/environments/staging.rb +10 -0
  37. data/config/environments/test.rb +12 -0
  38. data/config/init.rb +26 -0
  39. data/config/rack.rb +11 -0
  40. data/config/router.rb +41 -0
  41. data/config/test.conf +2 -0
  42. data/doc/rdoc/generators/merb_generator.rb +1362 -0
  43. data/doc/rdoc/generators/template/merb/api_grease.js +640 -0
  44. data/doc/rdoc/generators/template/merb/index.html.erb +37 -0
  45. data/doc/rdoc/generators/template/merb/merb.css +252 -0
  46. data/doc/rdoc/generators/template/merb/merb.rb +351 -0
  47. data/doc/rdoc/generators/template/merb/merb_doc_styles.css +492 -0
  48. data/doc/rdoc/generators/template/merb/prototype.js +2515 -0
  49. data/lib/slashport.rb +93 -0
  50. data/public/favicon.ico +0 -0
  51. data/public/images/merb.jpg +0 -0
  52. data/public/javascripts/application.js +1 -0
  53. data/public/merb.fcgi +22 -0
  54. data/public/robots.txt +5 -0
  55. data/public/stylesheets/master.css +119 -0
  56. data/spec/requests/cfg_spec.rb +7 -0
  57. data/spec/requests/config_spec.rb +7 -0
  58. data/spec/requests/configdoc_spec.rb +7 -0
  59. data/spec/requests/var_spec.rb +7 -0
  60. data/spec/requests/vardoc_spec.rb +7 -0
  61. data/spec/spec.opts +0 -0
  62. data/spec/spec_helper.rb +20 -0
  63. metadata +156 -0
@@ -0,0 +1,35 @@
1
+ require 'rubygems'
2
+ require 'rake/rdoctask'
3
+
4
+ require 'merb-core'
5
+ require 'merb-core/tasks/merb'
6
+
7
+ include FileUtils
8
+
9
+ # Load the basic runtime dependencies; this will include
10
+ # any plugins and therefore plugin rake tasks.
11
+ init_env = ENV['MERB_ENV'] || 'rake'
12
+ Merb.load_dependencies(:environment => init_env)
13
+
14
+ # Get Merb plugins and dependencies
15
+ Merb::Plugins.rakefiles.each { |r| require r }
16
+
17
+ # Load any app level custom rakefile extensions from lib/tasks
18
+ tasks_path = File.join(File.dirname(__FILE__), "lib", "tasks")
19
+ rake_files = Dir["#{tasks_path}/*.rake"]
20
+ rake_files.each{|rake_file| load rake_file }
21
+
22
+ desc "Start runner environment"
23
+ task :merb_env do
24
+ Merb.start_environment(:environment => init_env, :adapter => 'runner')
25
+ end
26
+
27
+ require 'spec/rake/spectask'
28
+ require 'merb-core/test/tasks/spectasks'
29
+ desc 'Default: run spec examples'
30
+ task :default => 'spec'
31
+
32
+ ##############################################################################
33
+ # ADD YOUR CUSTOM TASKS IN /lib/tasks
34
+ # NAME YOUR RAKE FILES file_name.rake
35
+ ##############################################################################
@@ -0,0 +1,2 @@
1
+ class Application < Merb::Controller
2
+ end
@@ -0,0 +1,8 @@
1
+ class Cfg < Application
2
+
3
+ def index
4
+ self.content_type = :text
5
+ return SlashPort::Component.get_configs.to_yaml
6
+ end
7
+
8
+ end
@@ -0,0 +1,13 @@
1
+ class Exceptions < Merb::Controller
2
+
3
+ # handle NotFound exceptions (404)
4
+ def not_found
5
+ render :format => :html
6
+ end
7
+
8
+ # handle NotAcceptable exceptions (406)
9
+ def not_acceptable
10
+ render :format => :html
11
+ end
12
+
13
+ end
@@ -0,0 +1,27 @@
1
+ class Var < Application
2
+ def index
3
+ only_provides :json, :text, :pp
4
+
5
+ filter = Hash.new
6
+ params.each do |key, value|
7
+ # skip parameters from merb itself
8
+ next if ["action", "controller", "format", "id"].include?(key)
9
+ next if value == nil
10
+
11
+ # If it looks like a regex, treat it like one.
12
+ # That is, if the value is /something/ (begin and end with slash)
13
+ if value =~ /^\/.+\/$/
14
+ filter[key] = Regexp.new(value[1..-2]) rescue value
15
+ else
16
+ # otherwise, treat it like a literal string to full match.
17
+ filter[key] = Regexp.new("^#{Regexp.escape(value)}$")
18
+ end
19
+ end
20
+
21
+ # ensure filter isn't changed
22
+ filter.freeze
23
+
24
+ @attributes = SlashPort::Component.get_attributes(filter)
25
+ display @attributes
26
+ end
27
+ end
@@ -0,0 +1,7 @@
1
+ class Vardoc < Application
2
+
3
+ def index
4
+ render
5
+ end
6
+
7
+ end
@@ -0,0 +1,5 @@
1
+ module Merb
2
+ module CfgHelper
3
+
4
+ end
5
+ end # Merb
@@ -0,0 +1,5 @@
1
+ module Merb
2
+ module GlobalHelpers
3
+ # helpers defined here available to all views.
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Merb
2
+ module VarHelper
3
+
4
+ end
5
+ end # Merb
@@ -0,0 +1,5 @@
1
+ module Merb
2
+ module VardocHelper
3
+
4
+ end
5
+ end # Merb
@@ -0,0 +1,13 @@
1
+ module SlashPort
2
+ class Attribute
3
+ attr_reader :handler
4
+ attr_reader :doc
5
+ attr_reader :sortkeys
6
+
7
+ def initialize(handler, doc, sortkeys=[])
8
+ @handler = handler
9
+ @doc = doc
10
+ @sortkeys = sortkeys
11
+ end
12
+ end # class Attribute
13
+ end # module SlashPort
@@ -0,0 +1,206 @@
1
+ require "socket"
2
+
3
+ module SlashPort
4
+ class Component
5
+ @@subclasses = Array.new
6
+ @@components = Array.new
7
+
8
+ def attributes
9
+ return self.class.attributes
10
+ end
11
+
12
+ def configs
13
+ return self.class.configs
14
+ end
15
+
16
+ def get_attributes(filter=nil)
17
+ get_things(attributes, filter)
18
+ end
19
+
20
+ def get_configs(filter=nil)
21
+ get_things(configs, filter)
22
+ end
23
+
24
+ def _want(pattern, *values)
25
+ if pattern == nil
26
+ return true
27
+ end
28
+
29
+ want = false
30
+ values.each do |value|
31
+ #puts "Checking #{pattern.class}/#{pattern} against #{value.class}/#{value.inspect}"
32
+ if (pattern.is_a?(Regexp) and !!(value.to_s =~ pattern))
33
+ #puts "Match! =~"
34
+ want = true
35
+ break
36
+ elsif value == pattern
37
+ #puts "Match! =="
38
+ want = true
39
+ break
40
+ end
41
+ end
42
+ return want
43
+ end
44
+
45
+ def get_things(thing, filter=nil)
46
+ return unless _want(filter["component"], self.class.label)
47
+
48
+ data = []
49
+
50
+ thing.each do |section, var|
51
+ next unless _want(filter["section"], section)
52
+ results = self.send(var.handler)
53
+ next if results == nil
54
+ results = [results] if !results.is_a?(Array)
55
+
56
+ results.each do |result|
57
+ result.labels["component"] = self.class.label
58
+ result.labels["section"] = section
59
+ result.labels["host"] = Socket.gethostname
60
+
61
+ keep = true
62
+ filter.each do |filterkey,filtervalue|
63
+ want = _want(filtervalue, result.labels[filterkey],
64
+ result.data[filterkey])
65
+ #puts "Filter: #{want} => #{filterkey} #{filtervalue}"
66
+
67
+ if (!want)
68
+ keep = false
69
+ break
70
+ end
71
+ end
72
+
73
+ if keep
74
+ # if our filter includes a 'data' name, then we
75
+ # should strip all nonmatching data entries.
76
+ if (result.data.keys & filter.keys).length > 0
77
+ result.data.each_key do |key|
78
+ next if filter.has_key?(key)
79
+ #puts "Removing #{key} data"
80
+ result.data.delete(key)
81
+ end
82
+ end
83
+ data << result
84
+ end
85
+ end
86
+ end
87
+ return data
88
+ end
89
+
90
+ def path(*names)
91
+ return [self.class.label, *names].join("/")
92
+ end
93
+
94
+ # See Class#inherited for what this method
95
+ def self.inherited(subclass)
96
+ puts "#{subclass.name} inherits #{self.name}"
97
+ @@subclasses << subclass
98
+
99
+ if subclass.respond_to?(:class_initialize)
100
+ subclass.class_initialize
101
+ end
102
+ end # def self.inherited
103
+
104
+ # class-level to easily map a attribute name to a method
105
+ # arguments:
106
+ # :name => attribute name
107
+ # :handler => method handler name
108
+ # :doc => attribute documentation
109
+ # :sort => [optional] array of keys for sort (used with var.text output)
110
+ def self.attribute(options = {})
111
+ if options[:doc] == nil
112
+ raise "Attribute #{self.name}/#{name} has no description"
113
+ end
114
+ name = options[:name]
115
+ puts "#{self.name}: new attribute #{name}"
116
+ options[:sort] ||= []
117
+
118
+ # remember: this is a class-level instance attribute
119
+ @attributes[options[:name]] = Attribute.new(options[:handler], options[:doc], options[:sort])
120
+ end # def self.attribute
121
+
122
+ # class-level to easily map a variable name to a handler
123
+ def self.config(name, handler, description=nil)
124
+ if description == nil
125
+ raise "Config #{self.name}/#{name} has no description"
126
+ end
127
+ #puts "#{self.name}: new config #{name}"
128
+
129
+ # remember: this is a class-level instance variable
130
+ @configs[name] = Variable.new(handler, description)
131
+ end # def self.config
132
+
133
+ def self.configs(filter=nil)
134
+ return @configs
135
+ end
136
+
137
+ def self.attributes(filter=nil)
138
+ return @attributes
139
+ end
140
+
141
+ # class-level initialization. This is called when ruby first
142
+ # creates this class object, a hack made possible by
143
+ # overriding Class#inherited (see 'def inherited' above).
144
+ def self.class_initialize
145
+ #puts "#{self}::class_initialize"
146
+ # remember, this is a class-level instance attribute
147
+ @attributes = Registry.new
148
+ @configs = Registry.new
149
+ @label = self.name.split("::")[-1].downcase
150
+
151
+ # disable this component by default
152
+ #puts "Disabling component '#{@label}' (default action, you must enable it if you want to use it)"
153
+ #disable
154
+ enable
155
+ end # def.class_initialize
156
+
157
+ # Show me all subclasses of SlashPort::Component
158
+ def self.components
159
+ if @@components.length == 0
160
+ @@subclasses.each do |klass|
161
+ component = klass.new
162
+ @@components << component
163
+ end
164
+ end
165
+
166
+ return @@components
167
+ end # def self.components
168
+
169
+ def self.get_things(thing, filter=nil)
170
+ data = []
171
+ self.components.each do |component|
172
+ next unless component.class.is_enabled?
173
+ result = component.send("get_#{thing}", filter)
174
+ if result
175
+ data += result
176
+ end
177
+ end
178
+ return data
179
+ end
180
+
181
+ def self.get_attributes(filter=nil)
182
+ return self.get_things("attributes", filter)
183
+ end
184
+
185
+ def self.get_configs(filter=nil)
186
+ return self.get_things("configs", filter)
187
+ end
188
+
189
+ def self.label
190
+ return @label
191
+ end
192
+
193
+ def self.disable
194
+ @active = false
195
+ end
196
+
197
+ def self.enable
198
+ @active = true
199
+ end
200
+
201
+ def self.is_enabled?
202
+ return @active
203
+ end
204
+
205
+ end # class Component
206
+ end # module SlashPort
@@ -0,0 +1,40 @@
1
+ class SlashPort::Exec
2
+ def initialize(cmd)
3
+ @cmd = cmd
4
+ end
5
+
6
+ def run
7
+ output = `#{@cmd}`
8
+ code = $?.exitstatus
9
+
10
+ return [output, code]
11
+ end
12
+
13
+ def to_tuple
14
+ data = []
15
+
16
+ output, code = run
17
+ lines = output.split(/\r?\n/)
18
+
19
+ if lines.length == 0
20
+ tuple = SlashPort::Tuple.new
21
+ tuple.data["output-lines"] = lines.length
22
+ tuple.data["exit-code"] = code
23
+ data << tuple
24
+ end
25
+
26
+ lines.each do |line|
27
+ tuple = SlashPort::Tuple.new
28
+ tuple.data["exit-code"] = code
29
+ begin
30
+ tuple.data["value"] = Float(line)
31
+ rescue ArgumentError => e
32
+ tuple.labels["string"] = 1
33
+ tuple.data["value"] = line
34
+ end
35
+
36
+ data << tuple
37
+ end
38
+ return data
39
+ end
40
+ end
@@ -0,0 +1,4 @@
1
+ module SlashPort
2
+ class Registry < Hash
3
+ end
4
+ end
@@ -0,0 +1,31 @@
1
+
2
+ # A tuple is like a slashport row.
3
+ # Each tuple contains a set of labels and data.
4
+ # Each label or data is a key:value pair, allowing you
5
+ # to name each label and each data.
6
+ #
7
+ # For example, transmit packet counts for a network interface would have
8
+ # a label of 'interface=eth0', for example, and a data of 'txpackets=12345'
9
+ # Multiple labels are supported/encouraged, as are multiple data.
10
+ # Following the interface example, you could hold all metrics for a single
11
+ # network interface with a single Tuple: ie;
12
+ # labels: { "interface" => "eth0" }
13
+ # data: { "txpackets" => 298374, "rxpackets" => 7577, "speed" => 1000 }
14
+ #
15
+ # Labels are intended to represent attributes that will not change.
16
+ # Data are intended to represent attributes that can change.
17
+ class SlashPort::Tuple
18
+ attr_accessor :labels
19
+ attr_accessor :data
20
+ def initialize
21
+ @labels = Hash.new
22
+ @data = Hash.new
23
+ end
24
+
25
+ def to_json
26
+ return {
27
+ "labels" => @labels,
28
+ "data" => @data,
29
+ }.to_json
30
+ end
31
+ end
@@ -0,0 +1,125 @@
1
+ require 'rubygems'
2
+
3
+ class SlashPort::Component
4
+ class LinuxHost < SlashPort::Component
5
+ attribute :name => "uptime",
6
+ :handler => :Uptime,
7
+ :doc => "Uptime in seconds"
8
+
9
+ attribute :name => "interfaces",
10
+ :handler => :IfStats,
11
+ :sort => ["interface", "field"],
12
+ :doc => "Interface Statistics"
13
+
14
+ attribute :name => "memory",
15
+ :handler => :MemStats,
16
+ :doc => "Memory stats from /proc/meminfo"
17
+
18
+ attribute :name => "disk",
19
+ :handler => :DiskStats,
20
+ :doc => "Disk stats from 'df'"
21
+
22
+ attribute :name => "load",
23
+ :handler => :LoadAverage,
24
+ :doc => "System load average reported by uptime(1)"
25
+
26
+ def _reapchild
27
+ begin
28
+ Process.wait(-1, Process::WNOHANG)
29
+ rescue Errno::ECHILD
30
+ # ignore
31
+ end
32
+ end
33
+
34
+ def Uptime
35
+ tuple = SlashPort::Tuple.new
36
+ tuple.data["uptime"] = File.open("/proc/uptime").read().split(" ")[0]
37
+ return tuple
38
+ end
39
+
40
+ def IfStats
41
+ data = Array.new
42
+ File.open("/proc/net/dev").readlines().each do |line|
43
+ line.chomp!
44
+ next if line =~ /^Inter-|^ face/
45
+
46
+ tuple = SlashPort::Tuple.new
47
+
48
+ fields = %w[rx_bytes rx_packets rx_errors rx_drop rx_fifo rx_frame
49
+ rx_compressed rx_multicast tx_bytes tx_packets tx_errors
50
+ tx_drop tx_fifo tx_colls tx_carrier tx_compressed]
51
+
52
+ interface, values = line.split(":")
53
+ interface.gsub!(/\s+/, "")
54
+
55
+ tuple.labels["interface"] = interface
56
+ fields.zip(values.split).each do |field,value|
57
+ tuple.data[field] = value
58
+ end
59
+ data << tuple
60
+ end
61
+ return data
62
+ end
63
+
64
+ def MemStats
65
+ data = Array.new
66
+ tuple = SlashPort::Tuple.new
67
+ File.open("/proc/meminfo").readlines().each do |line|
68
+ line.chomp!
69
+ key, value, unit = line.split(/[: ]+/)
70
+ value = value.to_i
71
+ if unit == "kB"
72
+ value *= 1024
73
+ end
74
+
75
+ tuple.data[key.downcase] = value
76
+ end
77
+ data << tuple
78
+ return data
79
+ end # def MemStats
80
+
81
+ def DiskStats
82
+ data = Array.new
83
+ IO.popen("df -PlB 1").readlines().each do |line|
84
+ # skip header
85
+ next if line =~ /Filesystem\s/
86
+ # skip nonpath sources (tmpfs, udev, etc)
87
+ next unless line =~ /^\//
88
+
89
+ line.chomp!
90
+ tuple = SlashPort::Tuple.new
91
+ fields = %w[size used available percentused]
92
+ values = line.split
93
+ source = values.shift
94
+ mount = values.pop
95
+
96
+ tuple.labels["source"] = source
97
+ tuple.labels["mount"] = mount
98
+ fields.zip(values).each do |field,value|
99
+ if field == "percentused"
100
+ value = value.to_i / 100.0
101
+ end
102
+
103
+ tuple.data[field] = value
104
+ end
105
+ data << tuple
106
+ end
107
+ _reapchild
108
+ return data
109
+ end # def DiskStats
110
+
111
+ def LoadAverage
112
+ data = Array.new
113
+ tuple = SlashPort::Tuple.new
114
+ loads = %x{uptime}.chomp.delete(",").split(/ +/)[-3..-1].map { |x| x.to_f }
115
+ _reapchild
116
+ load1, load5, load15 = loads
117
+ tuple.data["load-1min"] = load1
118
+ tuple.data["load-5min"] = load5
119
+ tuple.data["load-15min"] = load15
120
+
121
+ data << tuple
122
+ return data
123
+ end # def LoadAverage
124
+ end # class LinuxHost
125
+ end # class SlashPort::Component