jah 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,7 @@
1
+ *.sw?
2
+ *.log
3
+ .DS_Store
4
+ coverage
5
+ rdoc
6
+ pkg
7
+ jah.yaml
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Marcos Piccinini
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,115 @@
1
+ # ![Jah](http://fireho.com/system/jahlogo.png)
2
+
3
+ Talk to your machines. Like a god.
4
+
5
+ ## Install
6
+
7
+ Just run it for the first time to create a config file.
8
+ Use `jah install` to force the wizard again.
9
+
10
+ Gems needed for XMPP mode:
11
+
12
+ * EventMachine (gem install eventmachine)
13
+ * Blather (gem install blather)
14
+
15
+
16
+ Jah will try to find a `jah.yaml` file on ~/.jah or /etc.
17
+
18
+
19
+ ## Modes
20
+
21
+ ### XMPP
22
+
23
+ Chat with your server with your favorite XMPP client (pidgin, adium..)
24
+ Receive reports and failures realtime!
25
+ Send commands and
26
+
27
+
28
+ ### POST
29
+
30
+ The old school way. Set up a Jah Web Server, and give Jah it`s address.
31
+ Jah will periodically post info. You can send commands through SSH.
32
+
33
+
34
+ ### DUMP
35
+
36
+ AkA: Security freak. Jah just writes to tmp/ or whatever a dump file,
37
+ Jah Web securely connects (scp) and downloads the data to parse.
38
+
39
+
40
+ ## Use God?
41
+
42
+
43
+ ## XMPP Bot
44
+
45
+
46
+ Execute sh commands:
47
+
48
+ pwd
49
+ $> /home/jah
50
+
51
+
52
+ Executing ruby statements:
53
+
54
+ ! 2 + 2
55
+ => 4
56
+
57
+ ! def foo; "hi"; end
58
+ ! foo
59
+ => "hi"
60
+
61
+
62
+ Execute Jah commands:
63
+
64
+ cpu?
65
+ msweet: 0.14, 0.25, 0.14
66
+
67
+ mem?
68
+ msweet: 53%
69
+
70
+ net?
71
+ 7 connections
72
+ [ips...]
73
+
74
+ ok?
75
+ [Personalized cool phrase...]
76
+
77
+
78
+ Group session:
79
+
80
+ me: ok?
81
+ msweet: I'm fine, thanks..
82
+ ssaint: Kinda busy right now..
83
+ naomi: I need you! Now!
84
+
85
+
86
+ Execute God statements:
87
+
88
+ Start, stop, restart, monitor and unmonitor words on the beginning
89
+ of a phrase will make Jah add a "god" on the front of it, making it
90
+ trivial to work with your services:
91
+
92
+
93
+ start nginx
94
+ restart nanites
95
+ unmonitor postgresql
96
+
97
+
98
+ More to come...
99
+
100
+
101
+
102
+ ## Note on Patches/Pull Requests
103
+
104
+ * Fork the project.
105
+ * Make your feature addition or bug fix.
106
+ * Add tests for it. This is important so I don't break it in a
107
+ future version unintentionally.
108
+ * Commit, do not mess with rakefile, version, or history.
109
+ (if you want to have your own version, that is fine but
110
+ bump version in a commit by itself I can ignore when I pull)
111
+ * Send me a pull request. Bonus points for topic branches.
112
+
113
+ ## Copyright
114
+
115
+ Copyright (c) 2009 Marcos Piccinini. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,75 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "jah"
8
+ gem.summary = "be omnipresent..."
9
+ gem.description = "talk to your machines"
10
+ gem.email = "x@nofxx.com"
11
+ gem.homepage = "http://github.com/nofxx/jah"
12
+ gem.authors = ["Marcos Piccinini"]
13
+ gem.add_dependency "blather"
14
+ gem.add_dependency "i18n"
15
+ gem.add_development_dependency "rspec"
16
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
17
+ end
18
+ rescue LoadError
19
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
20
+ end
21
+
22
+ require 'spec/rake/spectask'
23
+ Spec::Rake::SpecTask.new(:spec) do |spec|
24
+ spec.libs << 'lib' << 'spec'
25
+ spec.spec_files = FileList['spec/**/*_spec.rb']
26
+ end
27
+
28
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
29
+ spec.libs << 'lib' << 'spec'
30
+ spec.pattern = 'spec/**/*_spec.rb'
31
+ spec.rcov = true
32
+ end
33
+
34
+ task :spec => :check_dependencies
35
+ task :default => :spec
36
+
37
+ begin
38
+ require 'reek/rake_task'
39
+ Reek::RakeTask.new do |t|
40
+ t.fail_on_error = true
41
+ t.verbose = false
42
+ t.source_files = 'lib/**/*.rb'
43
+ end
44
+ rescue LoadError
45
+ task :reek do
46
+ abort "Reek is not available. In order to run reek, you must: sudo gem install reek"
47
+ end
48
+ end
49
+
50
+ begin
51
+ require 'roodi'
52
+ require 'roodi_task'
53
+ RoodiTask.new do |t|
54
+ t.verbose = false
55
+ end
56
+ rescue LoadError
57
+ task :roodi do
58
+ abort "Roodi is not available. In order to run roodi, you must: sudo gem install roodi"
59
+ end
60
+ end
61
+
62
+
63
+ require 'rake/rdoctask'
64
+ Rake::RDocTask.new do |rdoc|
65
+ if File.exist?('VERSION')
66
+ version = File.read('VERSION')
67
+ else
68
+ version = ""
69
+ end
70
+
71
+ rdoc.rdoc_dir = 'rdoc'
72
+ rdoc.title = "jah #{version}"
73
+ rdoc.rdoc_files.include('README*')
74
+ rdoc.rdoc_files.include('lib/**/*.rb')
75
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.3
data/bin/jah ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $VERBOSE = true # -w
4
+ $KCODE = "u" # -Ku
5
+
6
+ $LOAD_PATH << File.join(File.dirname(__FILE__), *%w[.. lib])
7
+ require "jah"
8
+
9
+ Jah::Cli.dispatch(ARGV)
data/jah.gemspec ADDED
@@ -0,0 +1,89 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{jah}
8
+ s.version = "0.0.3"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Marcos Piccinini"]
12
+ s.date = %q{2009-09-29}
13
+ s.default_executable = %q{jah}
14
+ s.description = %q{talk to your machines}
15
+ s.email = %q{x@nofxx.com}
16
+ s.executables = ["jah"]
17
+ s.extra_rdoc_files = [
18
+ "LICENSE",
19
+ "README.md"
20
+ ]
21
+ s.files = [
22
+ ".document",
23
+ ".gitignore",
24
+ "LICENSE",
25
+ "README.md",
26
+ "Rakefile",
27
+ "VERSION",
28
+ "bin/jah",
29
+ "jah.gemspec",
30
+ "lib/jah.rb",
31
+ "lib/jah.yaml.template",
32
+ "lib/jah/agent.rb",
33
+ "lib/jah/agents/dump.rb",
34
+ "lib/jah/agents/post.rb",
35
+ "lib/jah/agents/xmpp.rb",
36
+ "lib/jah/cli.rb",
37
+ "lib/jah/collector.rb",
38
+ "lib/jah/collectors/cpu.rb",
39
+ "lib/jah/collectors/disk.rb",
40
+ "lib/jah/collectors/mem.rb",
41
+ "lib/jah/collectors/net.rb",
42
+ "lib/jah/collectors/prok.rb",
43
+ "lib/jah/collectors/services.rb",
44
+ "lib/jah/collectors/who.rb",
45
+ "lib/jah/command.rb",
46
+ "lib/jah/commands/admin.rb",
47
+ "lib/jah/commands/extra.rb",
48
+ "lib/jah/commands/pub.rb",
49
+ "lib/jah/commands/status.rb",
50
+ "lib/jah/god.rb",
51
+ "lib/jah/install.rb",
52
+ "lib/locales/en_us.yml",
53
+ "lib/locales/pt_br.yml",
54
+ "lib/locales/pt_br_gostosa.yml",
55
+ "lib/locales/pt_br_mano.yml",
56
+ "lib/locales/pt_br_mineiro.yml",
57
+ "spec/console",
58
+ "spec/jah_spec.rb",
59
+ "spec/spec_helper.rb"
60
+ ]
61
+ s.homepage = %q{http://github.com/nofxx/jah}
62
+ s.rdoc_options = ["--charset=UTF-8"]
63
+ s.require_paths = ["lib"]
64
+ s.rubygems_version = %q{1.3.5}
65
+ s.summary = %q{be omnipresent...}
66
+ s.test_files = [
67
+ "spec/jah_spec.rb",
68
+ "spec/spec_helper.rb"
69
+ ]
70
+
71
+ if s.respond_to? :specification_version then
72
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
73
+ s.specification_version = 3
74
+
75
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
76
+ s.add_runtime_dependency(%q<blather>, [">= 0"])
77
+ s.add_runtime_dependency(%q<i18n>, [">= 0"])
78
+ s.add_development_dependency(%q<rspec>, [">= 0"])
79
+ else
80
+ s.add_dependency(%q<blather>, [">= 0"])
81
+ s.add_dependency(%q<i18n>, [">= 0"])
82
+ s.add_dependency(%q<rspec>, [">= 0"])
83
+ end
84
+ else
85
+ s.add_dependency(%q<blather>, [">= 0"])
86
+ s.add_dependency(%q<i18n>, [">= 0"])
87
+ s.add_dependency(%q<rspec>, [">= 0"])
88
+ end
89
+ end
data/lib/jah/agent.rb ADDED
@@ -0,0 +1,128 @@
1
+ require 'socket'
2
+ require 'net/https'
3
+ require 'net/http'
4
+ require 'zlib'
5
+ require 'stringio'
6
+ # require 'eventmachine'
7
+
8
+ module Jah
9
+ class Agent
10
+ PID_FILE = File.join("/tmp", "jah.pid")
11
+ autoload :XmppAgent, "jah/agents/xmpp"
12
+ autoload :PostAgent, "jah/agents/xmpp"
13
+ autoload :DumpAgent, "jah/agents/xmpp"
14
+
15
+ def initialize(options=nil)#, config)
16
+ if Jah.daemon?
17
+ puts "Jah starting in background.."
18
+ fork do
19
+ daemonize
20
+ run_agent_run
21
+ end
22
+ exit
23
+ end
24
+ run_agent_run
25
+ end
26
+
27
+ def pid_file
28
+ @pidfile ||= PID_FILE
29
+ end
30
+
31
+ def run_agent_run
32
+ case Jah.mode # @mode
33
+ when "xmpp" then XmppAgent.new.run
34
+ when "post" then PostAgent.new.run
35
+ else
36
+ DumpAgent.new
37
+ end
38
+ end
39
+
40
+ def self.start
41
+ new
42
+ end
43
+
44
+ def self.stop
45
+ if File.exists?(PID_FILE)
46
+ pid = File.read(PID_FILE)
47
+ puts "Stopping #{pid}"
48
+ `kill #{pid}`
49
+ else
50
+ puts "Jah not running..."
51
+ exit
52
+ end
53
+ end
54
+
55
+ def self.restart
56
+ stop
57
+ start
58
+ end
59
+
60
+ def self.install
61
+ Install.new
62
+ end
63
+
64
+ def daemonize
65
+ begin
66
+ File.open(pid_file, File::CREAT|File::EXCL|File::WRONLY) do |pid|
67
+ pid.puts $$
68
+ end
69
+ at_exit do
70
+ begin
71
+ File.unlink(pid_file)
72
+ rescue
73
+ Log.error "Unable to unlink pid file: #{$!.message}"
74
+ end
75
+ end
76
+ rescue
77
+ pid = File.read(pid_file).strip.to_i rescue "unknown"
78
+ running = true
79
+ begin
80
+ Process.kill(0, pid)
81
+ if stat = File.stat(pid_file)
82
+ if mtime = stat.mtime
83
+ if Time.now - mtime > 25 * 60 # assume process is hung after 25m
84
+ Log.info "Trying to KILL an old process..."
85
+ Process.kill("KILL", pid)
86
+ running = false
87
+ end
88
+ end
89
+ end
90
+ rescue Errno::ESRCH
91
+ running = false
92
+ rescue
93
+ # do nothing, we didn't have permission to check the running process
94
+ end
95
+ if running
96
+ if pid == "unknown"
97
+ Log.warn "Could not create or read PID file. " +
98
+ "You may need to the path to the config directory. " +
99
+ "See: http://scoutapp.com/help#data_file"
100
+ else
101
+ Log.warn "Process #{pid} was already running"
102
+ end
103
+ exit
104
+ else
105
+ Log.info "Stale PID file found. Clearing it and reloading..."
106
+ File.unlink(pid_file) rescue nil
107
+ retry
108
+ end
109
+ end
110
+
111
+ self
112
+ end
113
+ end
114
+
115
+
116
+ # def do_something!
117
+ # if @mode == :online
118
+ # puts "online"
119
+ # post(@config[:server], nil, format_out)
120
+ # else
121
+ # puts "Writing log.."
122
+ # f = open("/tmp/jah.txt", "a")
123
+ # f.write(format_out)
124
+ # f.close
125
+ # end
126
+ # end
127
+
128
+ end
@@ -0,0 +1,5 @@
1
+ module Jah
2
+ class DumpAgent
3
+
4
+ end
5
+ end
@@ -0,0 +1,18 @@
1
+
2
+ module Jah
3
+ class PostAgent
4
+
5
+
6
+ def post(url, error, body, headers = Hash.new, &response_handler)
7
+ return unless url
8
+ request(url, response_handler, error) do |connection|
9
+ post = Net::HTTP::Post.new( url.path +
10
+ (url.query ? ('?' + url.query) : ''),
11
+ HTTP_HEADERS.merge(headers) )
12
+ post.body = body
13
+ connection.request(post)
14
+ end
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,194 @@
1
+ require "eventmachine"#autoload :Client,
2
+ require "blather/client/client"
3
+
4
+ module Jah
5
+ # include Blather::DSL
6
+ class XmppAgent
7
+ ROSTER = []
8
+ attr_reader :client
9
+
10
+
11
+ def initialize #(jid, key, server, port=5222, debug=false, report=0)
12
+ Blather.logger.level = Logger::DEBUG if Jah.debug
13
+ @report = Jah.report
14
+ @client = Blather::Client.setup Jah.jid, Jah.key, Jah.server, Jah.port
15
+ setup
16
+ self
17
+ end
18
+
19
+ def process_message(to, msg, type = :chat)
20
+ if comm = Jah::Command.find(msg)
21
+ puts "Commmand => #{comm} | #{msg}"
22
+ args = msg.split(" ")
23
+ body = comm[2].send(comm[0], *args[1..-1])
24
+ else
25
+ keywords = %w{ start stop restart monitor unmonitor }
26
+ kind = :god if msg =~ /#{keywords.join("|")}/
27
+ kind ||= msg =~ /^\!/ ? :ruby : :sh
28
+ body = case kind
29
+ when :ruby
30
+ puts "Executing ruby command => #{msg}"
31
+ "=> #{execute_ruby(msg)}"
32
+ when :sh
33
+ puts "Executing *sh command => #{msg}"
34
+ "$> #{execute_sh(msg)}"
35
+ when :god
36
+ puts "Executing god command => #{msg}"
37
+ "G> #{execute_sh('god ' + msg)}"
38
+ else "dunno what to do...."
39
+ end
40
+ end
41
+ #beautify(body)
42
+ [ Blather::Stanza::Message.new(to, body, type)]
43
+ rescue => e
44
+ "Something is wrong.. #{e}\n#{e.backtrace.join("\n")}"
45
+ end
46
+
47
+ # Naive.... to be improved
48
+ def execute_ruby(code)
49
+ keywords = / class | module | def | do | while | for /
50
+ if code =~ keywords
51
+ return "Unmatched ends" if code.scan(keywords).length != code.scan(/end(\s|$)/).length
52
+ end
53
+ return "Unmatched.." if code.scan(/\{|\(|\[/).length != code.scan(/\}|\)|\]/).length
54
+ return "Unmatched quotes.." if code.scan(/\"|\'/).length % 2 != 0
55
+ begin
56
+ eval(code[1..-1]) || "nil"
57
+ rescue => e
58
+ "Fail => #{e}"
59
+ end
60
+ end
61
+
62
+ def execute_sh(code)
63
+ return I18n.t(:no_sudo) if code =~ /sudo/
64
+ res = `#{code}`.chomp
65
+ ex = $?.exitstatus
66
+ if ex != 0
67
+ res << I18n.t(:exit_code, :ex => ex.to_s)
68
+ else
69
+ res = "\n" + res unless res.split("\n").length < 2
70
+ end
71
+ res
72
+ end
73
+
74
+ #TODO: need to write raw....
75
+ def beautify(txt)
76
+ #txt.gsub!(/\*(.*)\*/, "<span style=\"font-weight: bold;\">\\1</span>")
77
+ #txt.gsub!(/\/(.*)\//, "<em>\\1</em>") # Italic
78
+ #txt.gsub!(/\_(.*)\_/, "<span style=\"font-decoration: underline;\">\\1</span>")
79
+ #txt.gsub!(/\-(.*)\-/, "<span style=\"font-decoration: line-through;\">\\1</span>")
80
+ # <span style="font-size: large;">ok?</span>
81
+ # <span style="color: #FF0000;">ok?</span>
82
+ txt
83
+ end
84
+
85
+ # [:iq, :query, :roster, :disco_info, :disco_items, :message, :presence,
86
+ # :status, :subscription, :pubsub_node, :pubsub_affiliations,
87
+ # :pubsub_create, :pubsub_event, :pubsub_items, :pubsub_publish,
88
+ # :pubsub_retract, :pubsub_subscribe, :pubsub_subscription,
89
+ # :pubsub_subscriptions, :pubsub_unsubscribe, :pubsub_owner,
90
+ # :pubsub_delete, :pubsub_purge]
91
+
92
+
93
+ def setup
94
+ # return if client && client.setup?
95
+ client.register_handler(:ready) do
96
+ puts "Connected!"
97
+ ROSTER << [client.roster.items.keys, Jah.groups].flatten.uniq
98
+ ROSTER.flatten!
99
+ ROSTER.select { |j| j =~ /\@conference\./ }.each do |c|
100
+ presence = Blather::Stanza::Presence.new
101
+ presence.to = "#{c}/#{Jah.hostname}"
102
+ client.write presence
103
+ end
104
+ end
105
+
106
+ client.register_handler :subscription, :request? do |s|
107
+ if ROSTER.include?(s.from.stripped.to_s)
108
+ puts "[REQUEST] Approve #{s}"
109
+ client.write s.approve!
110
+ else
111
+ puts "[REQUEST] Refuse #{s}"
112
+ client.write s.refuse!
113
+ end
114
+ end
115
+
116
+ # client.register_handler :message, :chat?, :body => 'exit' do |m|
117
+ # client.write Blather::Stanza::Message.new(m.from, 'Exiting...')
118
+ # client.close
119
+ # end
120
+ #client.register_handler :roster, [],
121
+ #client.register_handler :message, :error?, :body do |m|
122
+ #client.register_handler :message, :headline?, :body do |m|
123
+ #client.register_handler :message, :normal?, :body do |m|
124
+
125
+ client.register_handler :pubsub_event, :items do |m|
126
+ puts "[PUBSUB] => #{m.inspect}"
127
+ client.write m.body
128
+ end
129
+
130
+ client.register_handler :message, :groupchat? do |m|
131
+ if m.body =~ /^!|^>|^\\/ && m.to_s !~ /x.*:delay/ #delay.nil?
132
+ puts "[GROUP] => #{m.inspect}"
133
+ for msg in process_message(m.from.stripped, m.body, :groupchat)
134
+ client.write msg
135
+ end
136
+ end
137
+ end
138
+
139
+ client.register_handler :message, :chat?, :body do |m|
140
+ if ROSTER.include?(m.from.stripped.to_s)
141
+ puts "[PVT] => #{m.inspect}"
142
+ for msg in process_message(m.from, m.body)
143
+ client.write msg
144
+ end
145
+ end
146
+ end
147
+
148
+ end
149
+
150
+
151
+ def run
152
+ puts "Starting Jah Client...#{client.jid.stripped}"
153
+ trap(:INT) { EM.stop }
154
+ trap(:TERM) { EM.stop }
155
+
156
+ EM.run do
157
+ client.run
158
+ if @report != 0
159
+ puts "will report..."
160
+ EM.add_periodic_timer(@report) do
161
+ puts " I'm ok.. "
162
+ client.write Blather::Stanza::Message.new(
163
+ "nofxx@Jah.host.com", Jah::Collector.quick_results)
164
+ end
165
+ end
166
+
167
+ end
168
+
169
+
170
+ end
171
+
172
+ end
173
+
174
+ end
175
+
176
+
177
+ # # when_ready do
178
+ # # roster.grouped.each do |group, items|
179
+ # # puts "#{'*'*3} #{group || 'Ungrouped'} #{'*'*3}"
180
+ # # items.each { |item| puts "- #{item.name} (#{item.jid})" }
181
+ # # puts
182
+ # # end
183
+ # # shutdown
184
+ # # end
185
+
186
+ # # message :chat?, :body do |m|
187
+ # # begin
188
+ # # say m.from, eval(m.body)
189
+ # # rescue => e
190
+ # # say m.from, e.inspect
191
+ # # end
192
+
193
+
194
+ # end