lpm 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,3 @@
1
+ == lpm
2
+
3
+ Put appropriate LICENSE for your project here.
data/README ADDED
@@ -0,0 +1,3 @@
1
+ == lpm
2
+
3
+ You should document your project here.
@@ -0,0 +1,45 @@
1
+ #
2
+ # To change this template, choose Tools | Templates
3
+ # and open the template in the editor.
4
+
5
+
6
+ require 'rubygems'
7
+ require 'rake'
8
+ require 'rake/clean'
9
+ require 'rake/gempackagetask'
10
+ require 'rake/rdoctask'
11
+ require 'rake/testtask'
12
+
13
+ spec = Gem::Specification.new do |s|
14
+ s.name = 'lpm'
15
+ s.version = '1.0.0'
16
+ s.has_rdoc = true
17
+ s.extra_rdoc_files = ['README', 'LICENSE']
18
+ s.summary = 'Linux Proc Monitor'
19
+ s.description = s.summary
20
+ s.author = 'Charles Cui'
21
+ s.email = 'zheng.cuizh@gmail.com'
22
+ s.executables = ['lpm']
23
+ s.files = %w(LICENSE README Rakefile) + Dir.glob("{bin,lib,spec,test}/**/*")
24
+ s.require_path = "lib"
25
+ s.bindir = "bin"
26
+ end
27
+
28
+ Rake::GemPackageTask.new(spec) do |p|
29
+ p.gem_spec = spec
30
+ p.need_tar = true
31
+ p.need_zip = true
32
+ end
33
+
34
+ Rake::RDocTask.new do |rdoc|
35
+ files =['README', 'LICENSE', 'lib/**/*.rb']
36
+ rdoc.rdoc_files.add(files)
37
+ rdoc.main = "README" # page to start on
38
+ rdoc.title = "lpm Docs"
39
+ rdoc.rdoc_dir = 'doc/rdoc' # rdoc output folder
40
+ rdoc.options << '--line-numbers'
41
+ end
42
+
43
+ Rake::TestTask.new do |t|
44
+ t.test_files = FileList['test/**/*.rb']
45
+ end
data/bin/lpm ADDED
@@ -0,0 +1,121 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #--
4
+ # Copyright (c) 2009, Charles Cui
5
+ # All rights reserved. Licensed under the BSD license. See LICENSE for details
6
+ #++
7
+
8
+ # $: << File.expand_path(File.join(File.dirname(__FILE__),"..","lib"))
9
+ require 'rubygems'
10
+ require 'socket'
11
+ require 'optparse'
12
+ require "logger"
13
+
14
+ require File.join(File.dirname(__FILE__),"..","lib","utils.rb")
15
+ require File.join(File.dirname(__FILE__),"..","lib","xiaoqian.rb")
16
+ require File.join(File.dirname(__FILE__),"..","lib","server.rb")
17
+
18
+ vars = {}
19
+ ARGV.options do
20
+ |opts|
21
+ opts.banner = <<DOC
22
+ XiaoQian [also named => L(inux) P(roc) M(onitor)] :
23
+ Mail/Gtalk: zheng.cuizh@gmail.com
24
+
25
+ Usage: #{File.basename($0)} [options] argv...
26
+ (sudo )lpm -t 15 --memcache=10.2.226.133:11011,10.2.226.133:11012 -P --pids=28261,28263 --log_level=1
27
+ DOC
28
+
29
+ opts.on_head("LPM optins useage:")
30
+
31
+ vars[:port] = 9876
32
+ opts.on("-p", "--port=PORT", Integer,
33
+ "lpm rest server requires a port number.","default port=#{vars[:port]}") {|vars[:port]|}
34
+
35
+ # separater
36
+ opts.on()
37
+
38
+ vars[:push] = false
39
+ opts.on("-P","--[no-]push",
40
+ "if push is given, the --memcache must be set,", # multiline description
41
+ "and a job will push server performance data to memcache server.",
42
+ "default --push=#{vars[:push]}.") {|vars[:push]|}
43
+
44
+ vars[:timer] = 10
45
+ vars[:default_timer] = 10
46
+ opts.on("-t","--timer=Second", Integer,
47
+ "periodic timer to push cache",
48
+ "default --timer=#{vars[:default_timer]}(second).") {|vars[:timer]|}
49
+
50
+ # array
51
+ opts.on("-m", "--memcache=[LIST,LIST]", Array,
52
+ "memcache list,such as : --memcache=10.2.226.38:11211,10.2.226.37:11211","memcache server(s) must be alive.") {|vars[:memcache]|}
53
+
54
+ vars[:log] = "/var/log/lpm.log"
55
+ opts.on("-l","--log=LogPath",
56
+ "default --log=#{vars[:log]}"){|vars[:log]|}
57
+
58
+ vars[:default_log_level] = 3
59
+ opts.on("--log_level=LogLevel",Integer,"1 => DEBUG","2 => INFO","3 => WARN","4 => ERROR","5 => FATAL",
60
+ "default --log_level=#{vars[:default_log_level]}"){|vars[:log_level]|}
61
+
62
+ opts.on("--pids=[PID,PID]", Array,
63
+ "pids list,such as : --pids=1234,2134,4321") {|vars[:pids]|}
64
+
65
+ # separater
66
+ opts.on_tail
67
+ opts.on_tail("common options:")
68
+
69
+ # no argument, shows at tail
70
+ opts.on_tail("--help", "show this message") {puts opts; exit}
71
+
72
+ # no argument
73
+ opts.on_tail("--version", "show version") do
74
+ puts "XiaoQian [L(inux) P(roc) M(onitor)] : 1.0.1"
75
+ exit
76
+ end
77
+ opts.parse!
78
+ end
79
+
80
+ $logger = nil
81
+
82
+ begin
83
+ # $logger = Logger.new(vars[:log])
84
+ Logger::Formatter::Format = "%s, [%s#%d] %5s -- %s: %s#{$/}"
85
+ $logger = Logger.new(vars[:log],'daily',1048576)
86
+ if vars[:log_level] && (vars[:log_level] >= 1 || vars[:log_level] <= 5) then
87
+ $logger.level = vars[:log_level]
88
+ else
89
+ $logger.level = vars[:default_log_level]
90
+ end
91
+ rescue Exception => e
92
+ $logger = nil
93
+ p "params error:#{e.to_s}"
94
+ p "can't access log file => #{vars[:log]}"
95
+ end
96
+
97
+ $logger.info "LPMServer is starting..."
98
+ $logger.info "Server parameters are below : #{vars.inspect}"
99
+ $logger.info "LPMServer will running @ 0.0.0.0:#{vars[:port]}"
100
+
101
+ Daemon.start do
102
+ pid = []
103
+ pid << Process.fork do
104
+ $logger.info "LPMServer is starting..."
105
+ LPMServer.run('0.0.0.0',vars[:port])
106
+ end
107
+
108
+ if pid[0] then
109
+ pid << Process.fork do
110
+ $logger.info "Xiaoqian is starting..."
111
+ if vars[:push] then
112
+ vars[:timer] = vars[:default_timer] if (vars[:timer].to_i < 0)
113
+ Xiaoqian.run(vars[:timer].to_i,{:pids=>vars[:pids],:caches=>vars[:memcache],:log=>$logger}) if $logger
114
+ end
115
+ end
116
+ end
117
+
118
+ $logger.info "LPMServer,Xiaoqian = #{pid.inspect}"
119
+ Process.waitall
120
+ $logger.info "LPM main process exit."
121
+ end
@@ -0,0 +1 @@
1
+ # relations with LPMP(Caichen) in memcache
@@ -0,0 +1,8 @@
1
+ require File.join(File.dirname(__FILE__),'lpm','proc.rb')
2
+ require File.join(File.dirname(__FILE__),'lpm','process.rb')
3
+ require File.join(File.dirname(__FILE__),'lpm','error.rb')
4
+ require File.join(File.dirname(__FILE__),'lpm','utils.rb')
5
+ require File.join(File.dirname(__FILE__),'utils.rb')
6
+ require File.join(File.dirname(__FILE__),'xiaoqian.rb')
7
+
8
+ raise PlatFormError,'The LPM works on Linux only.' unless RUBY_PLATFORM =~ /linux/
@@ -0,0 +1,3 @@
1
+ class NoPidExist < RuntimeError; end
2
+ class PlatFormError < RuntimeError; end
3
+ class CpuLineSizeError < RuntimeError; end
@@ -0,0 +1,67 @@
1
+ module LPM
2
+ class Proc
3
+
4
+ class << self
5
+ def cpuinfo(*arguments)
6
+ begin
7
+ cpus_info = IO.read('/proc/cpuinfo').split("#{$/*2}")
8
+ return cpus_info.map do |cpu_info|
9
+ h = {}
10
+ cpu_info.split($/).each do |line|
11
+ kv = line.split(/\s*\:\s*/)
12
+ h[kv[0]] = kv[1]
13
+ end
14
+ h
15
+ end
16
+ rescue Exception => e
17
+ # => do nothing
18
+ return "{\"exception\":\"#{e.class}\",\"msg\":\"#{e}\"}"
19
+ ensure
20
+ end
21
+ end
22
+
23
+ def meminfo(*arguments)
24
+ begin
25
+ mem_info = IO.read('/proc/meminfo')
26
+ h = {}
27
+ mem_info.split($/).each do |line|
28
+ kv = line.split(/\s*\:\s*/)
29
+ h[kv[0].to_sym] = kv[1].chomp(' kB')
30
+ end
31
+ h
32
+ rescue Exception => e
33
+ # => do nothing
34
+ return "{\"exception\":\"#{e.class}\",\"msg\":\"#{e}\"}"
35
+ ensure
36
+ end
37
+ end
38
+
39
+ def loadavg(*arguments)
40
+ begin
41
+ load_avg = IO.read('/proc/loadavg').split(/\s+/)
42
+ h = {}
43
+ h[:last] = load_avg.pop
44
+ h[:running],h[:total] = load_avg.pop.split(/\//)
45
+ h[:load] = load_avg
46
+ h
47
+ rescue Exception => e
48
+ # => do nothing
49
+ return "{\"exception\":\"#{e.class}\",\"msg\":\"#{e}\"}"
50
+ ensure
51
+ end
52
+ end
53
+
54
+ def uptime(*arguments)
55
+ begin
56
+ total,idle = IO.read('/proc/uptime').split
57
+ {:total=>total,:idle=>idle}
58
+ rescue Exception => e
59
+ # => do nothing
60
+ return "{\"exception\":\"#{e.class}\",\"msg\":\"#{e}\"}"
61
+ ensure
62
+ end
63
+ end
64
+ end
65
+
66
+ end
67
+ end
@@ -0,0 +1,136 @@
1
+
2
+
3
+ module LPM
4
+ class Process
5
+ attr :pid
6
+ def initialize pid
7
+ @pid = pid
8
+ @pwd = "/proc/#{@pid}"
9
+ raise NoPidExist,"The process which pid is #{@pid} does not exist." unless File.directory? @pwd
10
+ end
11
+
12
+ def attr(*arguments)
13
+ h = {}
14
+ begin
15
+ Dir.chdir("#{@pwd}/attr")
16
+ Dir.entries("#{@pwd}/attr").each do |d|
17
+ next if (d == '.') || (d == '..')
18
+ h[d.to_sym] = IO.read("#{@pwd}/attr/#{d}")
19
+ end
20
+ rescue Exception => e
21
+ # => do nothing
22
+ h = "{\"exception\":\"#{e.class}\",\"msg\":\"#{e}\"}"
23
+ end
24
+ h
25
+ end
26
+
27
+ def cwd(*arguments)
28
+ begin
29
+ File.readlink("#{@pwd}/cwd")
30
+ rescue Exception => e
31
+ # => do nothing
32
+ "{\"exception\":\"#{e.class}\",\"msg\":\"#{e}\"}"
33
+ end
34
+ end
35
+
36
+ def exe(*arguments)
37
+ begin
38
+ File.readlink("#{@pwd}/exe")
39
+ rescue Exception => e
40
+ # => do nothing
41
+ "{\"exception\":\"#{e.class}\",\"msg\":\"#{e}\"}"
42
+ end
43
+ end
44
+
45
+ def root(*arguments)
46
+ begin
47
+ File.readlink("#{@pwd}/root")
48
+ rescue Exception => e
49
+ # => do nothing
50
+ "{\"exception\":\"#{e.class}\",\"msg\":\"#{e}\"}"
51
+ end
52
+ end
53
+
54
+ def fd(*arguments)
55
+ r = []
56
+ begin
57
+ Dir.chdir("#{@pwd}/fd")
58
+ Dir.entries("#{@pwd}/fd").each do |d|
59
+ next if (d == '.') || (d == '..')
60
+ if FileTest.symlink?(d)
61
+ r << {d.to_sym => File.readlink(d)}
62
+ else
63
+ r << {d.to_sym => d}
64
+ end
65
+ end
66
+ rescue Exception => e
67
+ # => do nothing
68
+ r = "{\"exception\":\"#{e.class}\",\"msg\":\"#{e}\"}"
69
+ ensure
70
+ r
71
+ end
72
+ end
73
+
74
+ def smaps(*arguments)
75
+ r = []
76
+ begin
77
+ _fd = File.open("#{@pwd}/#{smaps}",'r')
78
+ _ls = _fd.readlines
79
+
80
+ while(_ls!=[])
81
+ h = {}
82
+ 8.times do
83
+ k,v = _ls.shift.split(/\s*\:\s*/)
84
+ h[k.to_sym] = v
85
+ end
86
+ r << h
87
+ end
88
+
89
+ rescue Exception => e
90
+ # => do nothing
91
+ r = "{\"exception\":\"#{e.class}\",\"msg\":\"#{e}\"}"
92
+ ensure
93
+ _fd.close
94
+ end
95
+ r
96
+ end
97
+
98
+ def status(*arguments)
99
+ begin
100
+ _status = IO.read("#{@pwd}/status").split($/)
101
+ h = {}
102
+ _status.each do |line|
103
+ kv = line.split(/\s*\:\s*/)
104
+ h[kv[0].to_sym] = kv[1]
105
+ end
106
+ h
107
+ rescue Exception => e
108
+ # => do nothing
109
+ h = "{\"exception\":\"#{e.class}\",\"msg\":\"#{e}\"}"
110
+ ensure
111
+ end
112
+ end
113
+
114
+ def method_missing(method_name,*args)
115
+ begin
116
+ IO.read("#{@pwd}/#{method_name}")
117
+ rescue Exception => e
118
+ # => do nothing
119
+ "{\"exception\":\"#{e.class}\",\"msg\":\"#{e}\"}"
120
+ end
121
+ end
122
+ end
123
+
124
+ module ClassMethods
125
+
126
+ end
127
+
128
+ module InstanceMethods
129
+
130
+ end
131
+
132
+ def self.included(receiver)
133
+ receiver.extend ClassMethods
134
+ receiver.send :include, InstanceMethods
135
+ end
136
+ end
@@ -0,0 +1,170 @@
1
+ module LPM
2
+
3
+ # you could use utils like this:
4
+ # LPM::Utils.cpu(5)
5
+ # => {"cpu"=>100.0, "cpu0"=>100.0, "cpu1"=>100.0}
6
+
7
+ # also could use Utils as below:
8
+ # LPM::Proc.cpu(5)
9
+ # => {"cpu"=>100.0, "cpu0"=>100.0, "cpu1"=>100.0}
10
+ module Utils
11
+ module Proc
12
+ def pids(*arguments)
13
+ Dir.chdir '/proc'
14
+ Dir['[0-9]*'].inject([]) { |r, p|
15
+ r << {:pid => p, :cmdline => IO.read("#{p}/cmdline").gsub(/\000/,' ')}
16
+ }
17
+ end
18
+
19
+ def date(*arguments)
20
+ `date +"%F %T"`.chomp
21
+ end
22
+
23
+ def df(*arguments)
24
+ size,use,avai = 0,0,0
25
+ str = `df`
26
+ arr = str.split($/)
27
+ arr.shift
28
+ arr.each { |l| x = l.split; size += x[1].to_i; use += x[2].to_i; avai += x[3].to_i }
29
+ {:size => size,:use => use,:avai => avai}
30
+ end
31
+
32
+ # ulimit -n is the most useful.
33
+ # it limitted the count of total available socket.
34
+ def ulimit(*arguments)
35
+ `ulimit -a`.split($/).map do |line|
36
+ a,b = line.split(/\s*\(.*?\)\s*/)
37
+ {a=>b}
38
+ end
39
+ end
40
+
41
+ # attention: sysctl seperator is ':' in macos.
42
+ def sysctl(*arguments)
43
+ `sysctl -a`.split($/).map do |line|
44
+ a,b = line.split(/\s*=\s*/)
45
+ {a=>b}
46
+ end
47
+ end
48
+
49
+ def locale(*arguments)
50
+ `locale`.split($/).map do |line|
51
+ a,b = line.split(/\s*=\s*/)
52
+ {a=>b}
53
+ end
54
+ end
55
+
56
+ # get cpu useage.
57
+ # total_0=USER[0]+NICE[0]+SYSTEM[0]+IDLE[0]+IOWAIT[0]+IRQ[0]+SOFTIRQ[0]
58
+ # sleep x
59
+ # total_1=USER[1]+NICE[1]+SYSTEM[1]+IDLE[1]+IOWAIT[1]+IRQ[1]+SOFTIRQ[1]
60
+ # cpu_usage=(IDLE[0]-IDLE[1]) / (total_0-total_1) * 100
61
+
62
+ # scale is 100
63
+ # cpu(5) => {"cpu3"=>99.3333333333333, "cpu"=>99.75, "cpu0"=>100.0, "cpu1"=>100.0, "cpu2"=>99.3355481727575}
64
+
65
+ def cpu(interv=3,debug=false,*arguments)
66
+ begin
67
+ interv = interv.to_i
68
+ rescue NoMethodError => e
69
+ interv = 3
70
+ end
71
+
72
+ def getCpuStat(debug=false)
73
+ h = {}
74
+ IO.read('/proc/stat').split($/).map do|line|
75
+ a = line.split
76
+ # p "line => #{a.inspect}" if debug
77
+ # user nice system idle iowait irq softirq
78
+ #cpu 1961269 660 285319 1290544224 1522385 2383 0
79
+ if(a[0]=~/cpu/)
80
+ # raise CpuLineSizeError,"The count of words each line is not equal 8." if a.size != 8
81
+ # p "cpu line => #{a.inspect}" if debug
82
+ h[a[0]] = {:total => a[1..-1].inject(0) do |sum,i|
83
+ sum += i.to_f
84
+ end,
85
+ :idle => a[4].to_f}
86
+ # p "idle => #{h[a[0]][:idle]}" if debug
87
+ end
88
+ end
89
+ h
90
+ end
91
+
92
+ def getCpuUseage(h1,h2,debug=false)
93
+ # p h1.inspect if debug
94
+ # p h2.inspect if debug
95
+ # p "h2[:idle]-h1[:idle] = #{(h2[:idle]-h1[:idle]).to_f}" if debug
96
+ # p "h2[:total]-h1[:total] = #{(h2[:total]-h1[:total]).to_f}" if debug
97
+ 100 - (h2[:idle]-h1[:idle]).to_f/(h2[:total]-h1[:total]).to_f * 100
98
+ end
99
+
100
+ cpu_old,cpu_new = {},{}
101
+
102
+ th = Thread.fork do
103
+ cpu_old = getCpuStat(debug)
104
+ sleep interv
105
+ cpu_new = getCpuStat(debug)
106
+ end
107
+
108
+ th.join
109
+
110
+ r = {}
111
+ cpu_old.each do |k,v|
112
+ r[k.to_sym] =getCpuUseage(cpu_old[k],cpu_new[k],debug)
113
+ end
114
+ r
115
+ end
116
+
117
+ def hostname(*arguments)
118
+ `hostname`.chomp
119
+ end
120
+ end
121
+
122
+ # LPM::Process included the Process module,
123
+ # you can use as:
124
+ # => LPM::Process.new(pid).cpu
125
+ # then you will get that pid process's cpu total useage.
126
+ module Process
127
+
128
+ # rate = ((utime2+stime2) - (utime1+stime1)) / (total2 - total1)
129
+ # total = user + sys + idle + nice or get value from /proc/uptime
130
+ def cpu(interv=3,*arguments)
131
+ utime1,stime1,starttime1,total1,idle1 = nil
132
+ utime2,stime2,starttime2,total2,idle2 = nil
133
+
134
+ # t = Thread.fork do
135
+ utime1,stime1,starttime1 = IO.read("#{@pwd}/stat").split.values_at(15,16,23).map{|x|x.to_f}
136
+ # Attention : uptime file's end contain $/
137
+ # "3394903.84 3389669.77\n"
138
+ # if run in Thread,
139
+ # Process will hang on IO.read('/proc/uptime')
140
+ # like this => http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/125125
141
+ # i don't know why.
142
+ total1,idle1 = IO.read('/proc/uptime').chop.split.map{|x|x.to_f}
143
+ sleep interv
144
+ utime2,stime2,starttime2 = IO.read("#{@pwd}/stat").split.values_at(15,16,23).map{|x|x.to_f}
145
+ total2,idle2 = IO.read('/proc/uptime').chop.split.map{|x|x.to_f}
146
+ # end
147
+
148
+ # t.join
149
+ ((utime2+stime2) - (utime1+stime1)).to_f / (total2 - total1).to_f
150
+ end
151
+
152
+ def mem(*arguments)
153
+ (IO.read("#{@pwd}/statm").split.shift.to_f/1024).to_s
154
+ end
155
+ end
156
+
157
+ class << self;include Proc;end
158
+ end
159
+
160
+ # insert into LPM::Proc's meta class.
161
+ begin
162
+ (class << LPM::Proc;include Utils::Proc;end) unless LPM::Proc.include? Utils::Proc
163
+ LPM::Process.send :include,Utils::Process
164
+ rescue NameError
165
+ require File.join(File.dirname(__FILE__),'proc.rb')
166
+ require File.join(File.dirname(__FILE__),'process.rb')
167
+ retry
168
+ end
169
+
170
+ end
@@ -0,0 +1,70 @@
1
+ require 'rubygems'
2
+ require File.join(File.dirname(__FILE__),'lpm.rb')
3
+ require File.join(File.dirname(__FILE__),'utils.rb')
4
+
5
+ begin
6
+ gem 'sinatra'
7
+ require 'sinatra/base'
8
+ require 'activesupport' unless ({}.respond_to? :to_json)
9
+ rescue LoadError => e
10
+ p <<DOC
11
+ -----------------------------------------------------------------------
12
+ LPM say:
13
+ The LPM server require sinatra and activesupport.
14
+ Please exec "gem install sinatra" or "gem install activesupport" first.
15
+ -----------------------------------------------------------------------
16
+ Ruby say:
17
+ #{e}
18
+ -----------------------------------------------------------------------
19
+ DOC
20
+ exit(-1)
21
+ end
22
+
23
+ $LPMPool = {}
24
+
25
+ class AppServer < Sinatra::Base
26
+ get '/' do
27
+ "{\"msg\":\"Welcome using LPM(Linux Proc Monitor)\"}"
28
+ end
29
+
30
+ get '/lpm/:verbose' do
31
+ r = ''
32
+ case params[:verbose]
33
+ when /ver/i
34
+ r = "0.1.2";break;
35
+ when /proc/i
36
+ redirect '/proc'
37
+ when /process/i
38
+ redirect '/process'
39
+ end
40
+ r
41
+ end
42
+
43
+ get '/proc/:method/*' do
44
+ if params[:method] == nil then
45
+ "Monitor target is needed,such as <a href='/proc/cpuinfo'>/proc/cpuinfo</a>"
46
+ else
47
+ (LPM::Proc.send params[:method],*(params[:splat].split('/'))).to_json
48
+ end
49
+ end
50
+
51
+ get '/process/:pid/:method/*' do
52
+ if params[:method] != nil and params[:pid].to_i >= 0 then
53
+ begin
54
+ a_lpm = $LPMPool[params[:pid]]||($LPMPool[params[:pid]] = LPM::Process.new(params[:pid]))
55
+ (a_lpm.send params[:method],*(params[:splat].split('/'))).to_json
56
+ rescue NoPidExist => e
57
+ %Q-{"success":false,"msg":#{e}}-
58
+ end
59
+ else
60
+ "Monitor target and pid is needed,such as <a href='/process/909/status'>/proc/cpuinfo</a>"
61
+ end
62
+ end
63
+ end
64
+
65
+ class LPMServer
66
+ def self.run(host='0.0.0.0',port=9876)
67
+ # Daemon.start{AppServer.run! :host => host, :port => port}
68
+ AppServer.run! :host => host, :port => port
69
+ end
70
+ end
@@ -0,0 +1,13 @@
1
+ class Daemon
2
+ def Daemon.start
3
+ exit!(0) if fork
4
+ Process::setsid
5
+ exit!(0) if fork
6
+ Dir::chdir("/")
7
+ File::umask(0)
8
+ STDIN.reopen("/dev/null")
9
+ STDOUT.reopen("/dev/null", "w")
10
+ STDERR.reopen("/dev/null", "w")
11
+ yield if block_given?
12
+ end
13
+ end
@@ -0,0 +1,257 @@
1
+ require "rubygems"
2
+ require "eventmachine"
3
+ require "memcache"
4
+ require "logger"
5
+ require 'activesupport' unless ({}.respond_to? :to_json)
6
+
7
+ require File.join(File.dirname(__FILE__),'lpm.rb')
8
+
9
+ class Xiaoqian
10
+
11
+ # in memcache
12
+ # XQList:
13
+ # [
14
+ # '10.2.226.37',
15
+ # '10.2.226.38',
16
+ # '10.2.226.39'->pids:
17
+ # [
18
+ # 12345,
19
+ # 23456
20
+ # 34567
21
+ # ]
22
+ # ]
23
+
24
+ # based on pids in memcache,XiaoQian server will periodic update processes array.
25
+
26
+ # caches => ['10.2.226.38:11211','10.2.226.38:11212','10.2.226.39:11211']
27
+ # cacches => ['127.0.0.1:11011','127.0.0.1:11012']
28
+ # Xiaoqian.new(15,{:pids=>[28261,28263],:caches=>['10.2.226.133:11011','10.2.226.133:11012']})
29
+ def initialize(timer,opts = {:pids=>[],:caches=>[]})
30
+ @local_ip = get_local_ip
31
+ @timer = timer
32
+ @logger = opts[:log]
33
+
34
+ # @XQcaches = get_cache_clients(caches)
35
+ @XQcaches = MemCache.new(opts[:caches],:namespace=>"XQ#{@local_ip}")
36
+ @QNYHcaches = MemCache.new(opts[:caches],:namespace=>"QNYH")
37
+
38
+ @process = get_processes_instance(opts[:pids])
39
+ init_cache_config(opts[:pids],@local_ip)
40
+ end
41
+
42
+ def init_cache_config(pids,local_ip)
43
+ # load File.join(File.dirname(__FILE__),'config','stipulation.rb')
44
+ @XQcaches.set :localhost,local_ip
45
+ @XQcaches.set :pids,pids
46
+
47
+
48
+ register_QNYH_XQList
49
+ end
50
+
51
+ def register_QNYH_XQList
52
+ list = @QNYHcaches.get(:XQList) || []
53
+ unless list.include?(@local_ip) then
54
+ list << @local_ip
55
+ @QNYHcaches.set(:XQList,list)
56
+ end
57
+ list
58
+ end
59
+
60
+ def get_local_ip
61
+ require 'socket'
62
+ IPSocket.getaddress(Socket.gethostname)
63
+ end
64
+
65
+ # proIns =>
66
+ # {12345 => <LPM::Process>,67890 => <LPM::Process>}
67
+ def update_processes_instance(proIns={},pids=[])
68
+ keys = proIns.keys
69
+ pids.each do |pid|
70
+ begin
71
+ proIns[pid] = LPM::Process.new(pid) unless keys.include?(pid)
72
+ rescue Exception => e
73
+ @logger.error "update_processes_instance(#{proIns.inspect},#{pids.inspect})"
74
+ @logger.error e
75
+ end
76
+ end
77
+ keys = proIns.keys
78
+ (keys - pids).each{ |var| proIns.delete var }
79
+ proIns
80
+ end
81
+
82
+ def get_processes_instance(pids=[])
83
+ h = {}
84
+ if pids then
85
+ pids.each{ |pid|
86
+ begin
87
+ h[pid] = LPM::Process.new(pid)
88
+ rescue Exception => e
89
+ @logger.error "get_processes_instance(#{pids.inspect})"
90
+ @logger.error e
91
+ end
92
+ }
93
+ end
94
+ h
95
+ end
96
+
97
+ def get_cache_clients(caches=[])
98
+
99
+ # EM::P::Memcache.connect =>
100
+ # # Connect to a memcached server (must support NOREPLY, memcached >= 1.2.4)
101
+ # def self.connect host = 'localhost', port = 11211
102
+ # EM.connect host, port, self, host, port
103
+ # end
104
+ caches.inject([]){|arr,ca|
105
+ host,port = ca.split(':')
106
+ if port then
107
+ arr << EM::P::Memcache.connect(host,port.to_i)
108
+ else
109
+ arr << EM::P::Memcache.connect(host)
110
+ end
111
+ }
112
+ end
113
+
114
+ def wrap_data(data)
115
+ {:ts => Time.now.to_i,:data => data}
116
+ end
117
+
118
+ # Data from Job
119
+ # => {:ts=>123456789,:data=><Hash>}
120
+
121
+ def process_job(timer)
122
+ @logger.error "[Pid => #{Process.pid}] -- Process Job start."
123
+ EM.run{
124
+ EM.add_periodic_timer(timer){
125
+ @logger.info "process_job is called."
126
+ @process = update_processes_instance(@process,@XQcaches.get(:pids))
127
+ @logger.info "@process => #{@process.inspect}."
128
+ r = []
129
+ t = []
130
+
131
+ @process.each do |key,ins|
132
+ tmp = {}
133
+ t << Thread.fork{
134
+ tmp.update({:cpu => ins.cpu})
135
+ }
136
+ tmp.update({
137
+ :pid => ins.pid,
138
+ :cwd => ins.cwd,
139
+ :exe => ins.exe,
140
+ :root => ins.root,
141
+ :cmdline => ins.cmdline,
142
+ :memory => ins.mem
143
+ })
144
+ r << tmp
145
+ end
146
+ t.each{|x|x.join}
147
+ @logger.info "@process result => #{r.inspect}"
148
+ @XQcaches.set :host_process_grid,wrap_data({:root => r,:total => r.size}.to_json)
149
+ }
150
+ }
151
+ end
152
+
153
+ # LPM::Proc or LPM::Process data will be wraped in json format.
154
+ # This format must be fit to the require of LPMP's javascript.
155
+
156
+ def proc_job(timer)
157
+ @logger.error "[Pid => #{Process.pid}] -- Proc Job start."
158
+ EM.run{
159
+ # cpu
160
+ EM.add_periodic_timer(timer){
161
+ # cpu
162
+ @logger.debug "proc_job's cpu is called."
163
+ @XQcaches.set :host_proc_cpu,wrap_data({:value => LPM::Proc.cpu[:cpu].to_f,:time => Time.now.strftime("%m-%d %Hh:%Mm:%Ss")}.to_json)
164
+ }
165
+ # loadavg
166
+ EM.add_periodic_timer(timer){
167
+ # loadavg
168
+ @logger.debug "proc_job's loadavg is called."
169
+ @XQcaches.set :host_proc_loadavg,wrap_data({:value => LPM::Proc.loadavg[:load][0].to_f,:time => Time.now.strftime("%m-%d %Hh:%Mm:%Ss")}.to_json)
170
+ }
171
+ # meminfo
172
+ EM.add_periodic_timer(timer){
173
+ # meminfo
174
+ @logger.debug "proc_job's meminfo is called."
175
+ @XQcaches.set :host_proc_meminfo,wrap_data({:root => LPM::Proc.meminfo.inject([]){|a,e|
176
+ a << {:type => e[0],:count => e[1]}
177
+ },:time => Time.now.strftime("%m-%d %Hh:%Mm:%Ss")}.to_json)
178
+ }
179
+ # df
180
+ EM.add_periodic_timer(timer){
181
+ # df
182
+ @logger.debug "proc_job's df is called."
183
+ @XQcaches.set :host_proc_df,wrap_data({:root => LPM::Proc.df.inject([]){|a,e|
184
+ a << {:type => e[0],:count => e[1]}
185
+ },:time => Time.now.strftime("%m-%d %Hh:%Mm:%Ss")}.to_json)
186
+ }
187
+ # pids
188
+ EM.add_periodic_timer(timer){
189
+ # pids
190
+ @logger.debug "proc_job's pids is called."
191
+ @XQcaches.set :host_proc_pids,wrap_data(LPM::Proc.pids.map{ |e| {:text => e[:cmdline],:leaf => true,:cls => "file",:qtip => e[:pid]} }.to_json)
192
+ }
193
+
194
+ # grid
195
+ EM.add_periodic_timer(timer){
196
+ @logger.info "proc_job's grid is called."
197
+ tmp = {}
198
+ t = Thread.fork{
199
+ tmp.update({:cpu => LPM::Proc.cpu["cpu"]})
200
+ }
201
+ tmp.update({
202
+ :ip => @local_ip,
203
+ :hostname => LPM::Proc.hostname,
204
+ :loadavg => LPM::Proc.loadavg[:load][0],
205
+ :mem_total => LPM::Proc.meminfo[:MemTotal],#.chomp(' kB'),
206
+ :mem_free => LPM::Proc.meminfo[:MemFree],#.chomp(' kB'),
207
+ :uptime_total => LPM::Proc.uptime[:total],
208
+ :uptime_idle => LPM::Proc.uptime[:idle],
209
+ :disk_size => LPM::Proc.df[:size],
210
+ :disk_use => LPM::Proc.df[:use],
211
+ :date => LPM::Proc.date
212
+ })
213
+ t.join
214
+ @logger.info "proc_grid => #{tmp.inspect}."
215
+ @XQcaches.set :host_proc_grid,wrap_data(tmp)
216
+ }
217
+ }
218
+ end
219
+
220
+ def work_job
221
+ timer = @timer
222
+ @logger.error "work_job will run,and interval timer is #{timer}."
223
+ p = []
224
+ p << Process.fork{
225
+ begin
226
+ self.proc_job(timer)
227
+ rescue Exception => e
228
+ @logger.fatal "Proc Job fatal error!"
229
+ @logger.fatal e.to_s
230
+ end
231
+ }
232
+ if p[0] then
233
+ p << Process.fork{
234
+ begin
235
+ self.process_job(timer)
236
+ rescue Exception => e
237
+ @logger.fatal "Process Job fatal error!"
238
+ @logger.fatal e.to_s
239
+ end
240
+ }
241
+ end
242
+ @logger.error "work_job is running."
243
+ @logger.error "process and proc job's pid => #{p.inspect}."
244
+ Process.waitall
245
+ @logger.error "work_job shutdown."
246
+ end
247
+
248
+ def self.run(timer,opts = {:pids=>[],:caches=>[]})
249
+ begin
250
+ # Daemon.start{Xiaoqian.new(timer,{:pids=>opts[:pisd],:caches=>opts[:caches],:log=>opts[:log]}).work_job}
251
+ Xiaoqian.new(timer,{:pids=>opts[:pids],:caches=>opts[:caches],:log=>opts[:log]}).work_job
252
+ rescue Exception => e
253
+ opts[:log].error e
254
+ opts[:log].error "Xiaoqian.new(#{timer},#{{:pids=>opts[:pids],:caches=>opts[:caches],:log=>opts[:log]}}).work_job"
255
+ end
256
+ end
257
+ end
@@ -0,0 +1,38 @@
1
+ require "rubygems"
2
+ require "eventmachine"
3
+ require "memcache"
4
+
5
+ @cache = MemCache.new(['127.0.0.1:11011'],:namespace=>"XQ#{@local_ip}")
6
+
7
+ EM.run{
8
+ p 'EM.run 1st block.'
9
+ EM.add_periodic_timer(5){
10
+ @cache.set :Timer1,"Time now => #{Time.now.to_s}"
11
+ p 'set Timer1'
12
+ }
13
+
14
+ EM.add_periodic_timer(5){
15
+ puts @cache.get(:Timer1)
16
+ }
17
+
18
+ EM.add_periodic_timer(7){
19
+ @cache.set :Timer2,"Hello dear All. I'm logger :)"
20
+ p 'set Timer2'
21
+ }
22
+
23
+ EM.add_periodic_timer(7){
24
+ puts @cache.get(:Timer2)
25
+ }
26
+ }
27
+
28
+ EM.run{
29
+ p 'EM.run 2nd block.'
30
+ EM.add_periodic_timer(3){
31
+ @cache.set :Timer3,"Time now => #{Time.now.to_s}"
32
+ p 'set Timer1'
33
+ }
34
+
35
+ EM.add_periodic_timer(3){
36
+ puts @cache.get(:Timer1)
37
+ }
38
+ }
@@ -0,0 +1,17 @@
1
+ require File.join(File.dirname(__FILE__),'..','lib','lpm.rb')
2
+
3
+ require "Benchmark"
4
+ require "pp"
5
+
6
+ Benchmark.bm do |x|
7
+ x.report("CPU"){pp LPM::Proc.cpu}
8
+ x.report("CPU(5)"){pp LPM::Proc.cpu(5)}
9
+ x.report("CPU(10)"){pp LPM::Proc.cpu(10)}
10
+ x.report("CPU(15)"){pp LPM::Proc.cpu(15)}
11
+ end
12
+
13
+ # => below test is for LPM::Proc
14
+ pp "LPM::Proc.loadavg => #{LPM::Proc.loadavg}"
15
+ pp "LPM::Proc.uptime => #{LPM::Proc.uptime}"
16
+ pp "LPM::Proc.meminfo => #{LPM::Proc.meminfo}"
17
+ pp "LPM::Proc.cpuinfo => #{LPM::Proc.cpuinfo}"
@@ -0,0 +1,11 @@
1
+ require File.join(File.dirname(__FILE__),'..','lib','lpm.rb')
2
+
3
+ require "Benchmark"
4
+ require "pp"
5
+
6
+ # if the pid equal 12345
7
+ # then
8
+ # p1 = LPM::Process.new(12345)
9
+ # p1.cpu
10
+ # p1.cwd
11
+ # ...
@@ -0,0 +1,14 @@
1
+ require File.join(File.dirname(__FILE__),'..','lib','lpm.rb')
2
+
3
+ require "Benchmark"
4
+ require "pp"
5
+
6
+ # => below test is for LPM::Utils
7
+ pp "LPM::Utils.ulimit => #{LPM::Utils.ulimit}"
8
+ pp "LPM::Utils.locale => #{LPM::Utils.locale}"
9
+ pp "LPM::Utils.sysctl => #{LPM::Utils.sysctl}"
10
+
11
+ # => LPM::Utils can also be used as
12
+ pp "LPM::Proc.ulimit => #{LPM::Proc.ulimit}"
13
+ pp "LPM::Proc.locale => #{LPM::Proc.locale}"
14
+ pp "LPM::Proc.sysctl => #{LPM::Proc.sysctl}"
metadata ADDED
@@ -0,0 +1,72 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lpm
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Charles Cui
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-29 00:00:00 +08:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Linux Proc Monitor
17
+ email: zheng.cuizh@gmail.com
18
+ executables:
19
+ - lpm
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README
24
+ - LICENSE
25
+ files:
26
+ - LICENSE
27
+ - README
28
+ - Rakefile
29
+ - bin/lpm
30
+ - lib/config/stipulation.rb
31
+ - lib/lpm/error.rb
32
+ - lib/lpm/proc.rb
33
+ - lib/lpm/process.rb
34
+ - lib/lpm/utils.rb
35
+ - lib/lpm.rb
36
+ - lib/server.rb
37
+ - lib/utils.rb
38
+ - lib/xiaoqian.rb
39
+ - test/self_test/em_periodic.rb
40
+ - test/test_proc.rb
41
+ - test/test_process.rb
42
+ - test/test_utils.rb
43
+ has_rdoc: true
44
+ homepage:
45
+ licenses: []
46
+
47
+ post_install_message:
48
+ rdoc_options: []
49
+
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ version:
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: "0"
63
+ version:
64
+ requirements: []
65
+
66
+ rubyforge_project:
67
+ rubygems_version: 1.3.5
68
+ signing_key:
69
+ specification_version: 3
70
+ summary: Linux Proc Monitor
71
+ test_files: []
72
+