slugly 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2012 Action Verb, LLC.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19
+ IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,84 @@
1
+ Slugly
2
+ ======
3
+
4
+ Proxy for Wormly.com's health monitoring to enable monitoring of machines that
5
+ are behind firewalls or where you don't want to run Wormly's HTTP and PHP-based
6
+ monitoring script. Requires only Ruby on the proxy server and nothing at all on
7
+ the machines being monitored when the SSH-based back-end is used.
8
+
9
+
10
+ Configuring Slugly
11
+ ==================
12
+
13
+ Configuring Slugly is simple! Simply create a config.yaml like the example
14
+ shown below.
15
+
16
+ Slugly needs to know where to listen for incoming HTTP requests. The `host`
17
+ and `port` entries are for telling Slugly where it should be listening for
18
+ Wormly.com's health monitoring polls.
19
+
20
+ The `wormly_key` is your Wormly.com API key. This key is used for enabling and
21
+ directing Wormly.com's Linux health monitoring polls to the Slugly URLs for
22
+ that host.
23
+
24
+ Slugly serves up health information for each node according to the `nodes`
25
+ entry in the configuration file. A node entry consists of:
26
+
27
+ * a `path` specifying the URL where this health monitoring information should
28
+ be found.
29
+ * an optional `wormly_id` for this node.
30
+ * a `provider` for gathering the health-related metadata
31
+ * and any `options` required by this provider.
32
+
33
+ Specifying a valid `wormly_key` together with a `wormly_id` for a given node
34
+ will cause Slugly to attempt to notify Wormly.com that health data is
35
+ available at the corresponding Slugly URL. If either is omitted, invalid, or
36
+ if the Wormly.com API call fails, Slugly will still host metadata for this
37
+ node. However, Wormly may be unaware unless a successful API call is made by
38
+ other means.
39
+
40
+ Currently, there are two providers available:
41
+ * the `HealthProvider` which provides metadata for the local machine.
42
+ * and the `SSHHealthProvider` which gathers health metadata from a remote
43
+ Linux machine by way of SSH.
44
+
45
+ The `SSHHealthProvider` takes two additional options: the `hostname` of the
46
+ target machine and a `username` of a user on that machine. Slugly assumes that
47
+ password-less SSH is configured between the user running Slugly and the host
48
+ identified by `hostname`.
49
+
50
+ host: publichostname.com
51
+ port: 8088
52
+ wormly_key: 1Xs3cr3tk3ys
53
+ nodes:
54
+ - path : /ssh
55
+ wormly_id : 12345
56
+ provider : SSHHealthProvider
57
+ options:
58
+ hostname: localhost
59
+ username: ubuntu
60
+ - path : /local
61
+ wormly_id : 67890
62
+ provider : HealthProvider
63
+
64
+ Running Slugly
65
+ ==============
66
+
67
+ It's just `slugly -c /path/to/myconfig.yaml`. What could be simpler than that?
68
+
69
+ Notes on Security
70
+ =================
71
+ Slugly does not store passwords. Password-less SSH is required for use of the
72
+ SSHHealthProvider.
73
+
74
+ Slugly is a work in progress, and is not quite yet ready for use in a
75
+ production environment. In particular, the current version of Slugly has two
76
+ security-related weaknesses:
77
+
78
+ * The current version of Slugly does not have SSL enabled
79
+
80
+ * Slugly does not require that the Wormly.com supplied password be present or
81
+ correct. As a result Slugly openly provides health related metadata to any and
82
+ all who request it.
83
+
84
+
data/bin/slugly ADDED
@@ -0,0 +1,88 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), %w{.. lib}))
3
+
4
+ require 'optparse'
5
+ require 'uri'
6
+ require 'yaml'
7
+
8
+ require 'slugly/server'
9
+ require 'slugly/health_provider'
10
+ require 'slugly/ssh_health_provider'
11
+ require 'slugly/wormly_api'
12
+
13
+ options_h = {}
14
+
15
+ optparse = OptionParser.new do |options|
16
+ options.on('-c', '--config FILENAME', 'config file path') do |cfgfile|
17
+ options_h[:cfgfile] = cfgfile
18
+ end
19
+
20
+ options.on('-h', '--help', 'display this screen') do
21
+ puts options
22
+ exit
23
+ end
24
+ end
25
+
26
+ begin
27
+ optparse.parse!
28
+ required = [:cfgfile]
29
+ missing = required.select{ |param| options_h[param].nil? }
30
+ if not missing.empty?
31
+ puts "missing required options: #{missing.join(', ')}"
32
+ puts optparse
33
+ exit
34
+ end
35
+ rescue OptionParser::InvalidOption, OptionParser::MissingArgument
36
+ puts $!.to_s
37
+ puts optparse
38
+ exit
39
+ end
40
+
41
+ providers = {
42
+ 'HealthProvider' => HealthProvider,
43
+ 'SSHHealthProvider' => SSHHealthProvider
44
+ }
45
+
46
+ config = YAML.load_file(options_h[:cfgfile])
47
+ wormly_key = config['wormly_key']
48
+ host = config['host']
49
+ port = config['port']
50
+ nodes = config['nodes']
51
+
52
+ wormly_url_list = []
53
+
54
+ routes = {}
55
+ nodes.each do |node|
56
+ path = node['path']
57
+ provider = providers[node['provider']]
58
+ options = node['options']
59
+ wormly_id = node['wormly_id']
60
+
61
+ if wormly_id and wormly_key
62
+ uri = URI::HTTP.build({
63
+ :host => host, :port => port, :path => path
64
+ })
65
+
66
+ wormly_url_list.push([wormly_id, uri])
67
+ end
68
+
69
+ routes[path] = provider.new(options)
70
+ end
71
+
72
+ slugly_server = Server.new(host, port, routes)
73
+ slugly_server.serve() do
74
+ #once the server is running tell wormly we're up
75
+ #wormly verifies that the server responds so we avoid blocking with a new thread
76
+ Thread.new() do
77
+ wormly_api = WormlyAPI.new(wormly_key)
78
+
79
+ wormly_url_list.each do |wormly_id, uri|
80
+ res = wormly_api.enable_host_hm_linux(wormly_id, uri)
81
+ status = res.errorcode.zero? ? "succeeded" : "failed with error '#{res.errormsg}'"
82
+ puts "Enabling Health Monitoring for wormly_id #{wormly_id} at #{uri} #{status}."
83
+ end
84
+
85
+ end
86
+
87
+ end
88
+
@@ -0,0 +1,13 @@
1
+ wormly_key: 1Xs3cr3tk3ys
2
+ host: publichostname.com
3
+ port: 8088
4
+ nodes:
5
+ - path : /ssh
6
+ wormly_id : 12345
7
+ provider : SSHHealthProvider
8
+ options:
9
+ hostname: localhost
10
+ username: ubuntu
11
+ - path : /local
12
+ wormly_id : 67890
13
+ provider : HealthProvider
@@ -0,0 +1,52 @@
1
+ class HealthProvider
2
+
3
+ CMD_TIME = "date +%s | tr -d '\\n'"
4
+ CMD_UPTIME = "uptime"
5
+ CMD_MEMINFO = "cat /proc/meminfo"
6
+ CMD_UNAME = "uname -a"
7
+ CMD_DF = "df -P"
8
+ CMD_TRAFFIC = "cat /proc/net/dev"
9
+ CMD_VMSTAT_PRE = <<-EOS
10
+ if [ ! -f /tmp/slugly.work ];
11
+ then
12
+ nohup sh -c "vmstat 300 2 1>/tmp/slugly.work 2>&1; mv /tmp/slugly.work /tmp/slugly.vmstat" < /dev/null > /dev/null 2>&1 &
13
+ fi
14
+ EOS
15
+ CMD_VMSTAT = "cat /tmp/slugly.vmstat"
16
+ CMD_WORK_MTIME = "stat -c %Y /tmp/slugly.work"
17
+ CMD_WORK_CLEAN = "find /tmp -maxdepth 1 -mmin +5 -type f -name 'slugly.work' -exec rm -f /tmp/slugly.work \\;"
18
+
19
+ def initialize(options)
20
+ end
21
+
22
+ def time
23
+ %x{#{CMD_TIME}}
24
+ end
25
+ def uptime
26
+ %x{#{CMD_UPTIME}}
27
+ end
28
+ def meminfo
29
+ %x{#{CMD_MEMINFO}}
30
+ end
31
+ def uname
32
+ %x{#{CMD_UNAME}}
33
+ end
34
+ def df
35
+ %x{#{CMD_DF}}
36
+ end
37
+ def traffic
38
+ %x{#{CMD_TRAFFIC}}
39
+ end
40
+
41
+ def vmstat
42
+ %x{#{CMD_VMSTAT_PRE}}
43
+ %x{#{CMD_VMSTAT}}
44
+ end
45
+
46
+ def work_mtime
47
+ res = %x{#{CMD_WORK_MTIME}}
48
+ %x{#{CMD_WORK_CLEAN}}
49
+ res
50
+ end
51
+
52
+ end
@@ -0,0 +1,18 @@
1
+ require 'webrick'
2
+ require 'slugly/slugly_servlet'
3
+ require 'slugly/wormly_serializer0_91'
4
+
5
+ class Server
6
+ def initialize(host, port, routes)
7
+ @host = host
8
+ @port = port
9
+ @routes = routes
10
+ end
11
+
12
+ def serve(&f)
13
+ server = WEBrick::HTTPServer.new(:BindAddress => @host, :Port => @port, :StartCallback => f)
14
+ server.mount "/", SluglyServlet, @routes, WormlySerializer0_91.new()
15
+ ['INT', 'TERM'].each {|sig| trap(sig) {server.shutdown}}
16
+ server.start
17
+ end
18
+ end
@@ -0,0 +1,32 @@
1
+ require 'webrick'
2
+
3
+ class SluglyServlet < WEBrick::HTTPServlet::AbstractServlet
4
+ def initialize(server, providers, wormlyserializer)
5
+ super(server)
6
+ @serializer = wormlyserializer
7
+ @providers = providers
8
+ end
9
+
10
+ def do_POST(request, response)
11
+ response['Content-Type'] = 'text/plain'
12
+
13
+ if not @providers.has_key?(request.path)
14
+ response.status = 404
15
+ response.body = "Unknown Route"
16
+ else
17
+ begin
18
+ p = @providers[request.path]
19
+ response.body = @serializer.serialize(p)
20
+ response.status = 200
21
+ response['X-Wormly-Version'] = @serializer.version
22
+ rescue
23
+ response.body = "Internal Server Error"
24
+ response.status = 500
25
+ end
26
+ end
27
+ end
28
+
29
+ def do_GET(request, response)
30
+ do_POST(request, response)
31
+ end
32
+ end
@@ -0,0 +1,73 @@
1
+ require 'net/ssh'
2
+ require 'slugly/health_provider'
3
+
4
+ class SSHHealthProvider < HealthProvider
5
+ CACHE_TIME = 15
6
+ def initialize(options)
7
+ @hostname = options['hostname']
8
+ @username = options['username']
9
+ @last_update = Time.now - 2*CACHE_TIME
10
+ end
11
+
12
+ def time
13
+ update
14
+ @_time
15
+ end
16
+
17
+ def uptime
18
+ update
19
+ @_uptime
20
+ end
21
+
22
+ def meminfo
23
+ update
24
+ @_meminfo
25
+ end
26
+
27
+ def uname
28
+ update
29
+ @_uname
30
+ end
31
+
32
+ def df
33
+ update
34
+ @_df
35
+ end
36
+
37
+ def traffic
38
+ update
39
+ @_traffic
40
+ end
41
+
42
+ def vmstat
43
+ update
44
+ @_vmstat
45
+ end
46
+
47
+ def work_mtime
48
+ update
49
+ @_work_mtime
50
+ end
51
+
52
+ private
53
+
54
+ def update()
55
+ delta = (Time.now - @last_update).to_i
56
+ if delta >= CACHE_TIME
57
+ Net::SSH.start(@hostname, @username) do |ssh|
58
+ @_time = ssh.exec!(CMD_TIME)
59
+ @_uptime = ssh.exec!(CMD_UPTIME)
60
+ @_meminfo = ssh.exec!(CMD_MEMINFO)
61
+ @_uname = ssh.exec!(CMD_UNAME)
62
+ @_df = ssh.exec!(CMD_DF)
63
+ @_traffic = ssh.exec!(CMD_TRAFFIC)
64
+ ssh.exec!(CMD_VMSTAT_PRE)
65
+ @_vmstat = ssh.exec!(CMD_VMSTAT)
66
+ @_work_mtime = ssh.exec!(CMD_WORK_MTIME)
67
+ ssh.exec!(CMD_WORK_CLEAN)
68
+ @last_update = Time.now
69
+ end
70
+ end
71
+ end
72
+ end
73
+
@@ -0,0 +1,32 @@
1
+ require 'httparty'
2
+ require 'ostruct'
3
+
4
+ class WormlyAPI
5
+ include HTTParty
6
+
7
+ format :json
8
+ base_uri 'https://api.wormly.com/'
9
+
10
+ def initialize(key)
11
+ super()
12
+ @key = key
13
+ end
14
+
15
+ def enable_host_hm_linux(id, agenturl)
16
+ query('enableHostHMLinux', {:hostid => id, :agenturl => agenturl})
17
+ end
18
+
19
+ private
20
+ def query(cmd, args ={})
21
+ params = {
22
+ :key => @key,
23
+ :response => 'json',
24
+ :cmd => cmd
25
+ }
26
+
27
+ qargs = args.clone
28
+ qargs.update(params)
29
+
30
+ OpenStruct.new(self.class.post("/", :query=>qargs))
31
+ end
32
+ end
@@ -0,0 +1,25 @@
1
+ require 'slugly/wormly_serializer_base'
2
+
3
+ class WormlySerializer0_91 < WormlySerializerBase
4
+ def version
5
+ '0.91'
6
+ end
7
+
8
+ def serialize(provider)
9
+ meta1 = [
10
+ [:moment, provider.time],
11
+ [:version, version],
12
+ [:safe_mode, 0]
13
+ ]
14
+
15
+ s = stanza('meta-1', "", meta1)
16
+ s += stanza('top-1', provider.uptime)
17
+ s += stanza('vmstat-1', provider.vmstat)
18
+ s += stanza('ps-1', provider.work_mtime)
19
+ s += stanza('ram-1', provider.meminfo)
20
+ s += stanza('sysinfo-1', provider.uname)
21
+ s += stanza('dfdiskspace-1', provider.df)
22
+ s += stanza('traffic-1', provider.traffic)
23
+ s += "\n\n\n"
24
+ end
25
+ end
@@ -0,0 +1,17 @@
1
+ class WormlySerializerBase
2
+ def version
3
+ # e.g. '0.91'
4
+ end
5
+
6
+ def stanza(name, body = "", values ={})
7
+ s = "[#{name}]\n"
8
+ values.each do |k,v|
9
+ s += "#{k}:#{v}\n"
10
+ end
11
+ s += "#{body}\n"
12
+ end
13
+
14
+ def serialize(provider)
15
+ #serialize as per agent.php
16
+ end
17
+ end
metadata ADDED
@@ -0,0 +1,105 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: slugly
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 2
10
+ version: 0.0.2
11
+ platform: ruby
12
+ authors:
13
+ - Alfred Rossi
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-04-04 00:00:00 +00:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: net-ssh
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: httparty
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ hash: 3
44
+ segments:
45
+ - 0
46
+ version: "0"
47
+ type: :runtime
48
+ version_requirements: *id002
49
+ description: Proxy for Wormly.com's health monitoring to enable monitoring of machines that are behind firewalls or where you don't want to run Wormly's HTTP and PHP-based monitoring script. Requires only Ruby on the proxy server and nothing at all on the machines being monitored when the SSH-based backend is used.
50
+ email:
51
+ - alfred@actionverb.com
52
+ executables:
53
+ - slugly
54
+ extensions: []
55
+
56
+ extra_rdoc_files: []
57
+
58
+ files:
59
+ - bin/slugly
60
+ - lib/slugly/wormly_api.rb
61
+ - lib/slugly/ssh_health_provider.rb
62
+ - lib/slugly/health_provider.rb
63
+ - lib/slugly/slugly_servlet.rb
64
+ - lib/slugly/wormly_serializer_base.rb
65
+ - lib/slugly/server.rb
66
+ - lib/slugly/wormly_serializer0_91.rb
67
+ - LICENSE
68
+ - README.md
69
+ - config.yaml.example
70
+ has_rdoc: true
71
+ homepage: https://github.com/bombino/slugly
72
+ licenses: []
73
+
74
+ post_install_message:
75
+ rdoc_options: []
76
+
77
+ require_paths:
78
+ - lib
79
+ required_ruby_version: !ruby/object:Gem::Requirement
80
+ none: false
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ hash: 3
85
+ segments:
86
+ - 0
87
+ version: "0"
88
+ required_rubygems_version: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ hash: 3
94
+ segments:
95
+ - 0
96
+ version: "0"
97
+ requirements: []
98
+
99
+ rubyforge_project:
100
+ rubygems_version: 1.3.7
101
+ signing_key:
102
+ specification_version: 3
103
+ summary: Slugly is a health monitoring service for wormly with support for multiple backend machines.
104
+ test_files: []
105
+