slashport 0.15.10

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 (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