arbi 1.0.7 → 1.0.8
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.
- data/bin/arbi +3 -5
- data/bin/arbid +3 -11
- data/lib/arbi.rb +31 -256
- data/lib/arbi/cli/client.rb +89 -0
- data/lib/arbi/cli/server.rb +75 -0
- data/lib/arbi/client.rb +52 -0
- data/lib/arbi/config.rb +70 -0
- data/lib/arbi/modules.rb +183 -0
- data/lib/arbi/modules/acpi/adapter.rb +62 -0
- data/lib/arbi/modules/acpi/battery.rb +99 -0
- data/lib/arbi/modules/acpi/utils.rb +50 -0
- data/lib/arbi/modules/adapter.rb +22 -0
- data/lib/arbi/modules/battery.rb +22 -0
- data/lib/arbi/modules/cpu.rb +88 -0
- data/lib/arbi/modules/diskstat.rb +81 -0
- data/lib/arbi/modules/help.rb +40 -0
- data/lib/arbi/modules/net.rb +120 -0
- data/lib/arbi/modules/ram.rb +52 -0
- data/lib/arbi/modules/sys/adapter.rb +60 -0
- data/lib/arbi/modules/sys/battery.rb +101 -0
- data/lib/arbi/modules/sys/thermal.rb +71 -0
- data/lib/arbi/modules/version.rb +42 -0
- data/lib/arbi/server.rb +67 -0
- data/lib/arbi/timeline.rb +70 -0
- data/lib/arbi/utils/numeric.rb +81 -0
- data/lib/arbi/utils/table.rb +48 -0
- data/lib/arbi/version.rb +23 -0
- metadata +53 -10
- data/lib/arbi/common.rb +0 -3
- data/lib/arbi/plugins/batteries.rb +0 -85
- data/lib/arbi/plugins/cpu.rb +0 -89
- data/lib/arbi/plugins/diskspace.rb +0 -68
- data/lib/arbi/plugins/net.rb +0 -120
- data/lib/arbi/plugins/ram.rb +0 -29
- data/lib/arbi/plugins/thermal.rb +0 -71
data/lib/arbi/client.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
#--
|
2
|
+
# Copyleft shura. [ shura1991@gmail.com ]
|
3
|
+
#
|
4
|
+
# This file is part of arbi.
|
5
|
+
#
|
6
|
+
# arbi is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU Affero General Public License as published
|
8
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# arbi is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU Affero General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU Affero General Public License
|
17
|
+
# along with arbi. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
#++
|
19
|
+
|
20
|
+
require 'socket'
|
21
|
+
require 'arbi/modules'
|
22
|
+
|
23
|
+
module Arbi
|
24
|
+
|
25
|
+
class Client
|
26
|
+
attr_reader :sock
|
27
|
+
|
28
|
+
def initialize(address = '127.0.0.1', port = 6969)
|
29
|
+
@address = address || '127.0.0.1'
|
30
|
+
@port = port || 6969
|
31
|
+
|
32
|
+
self.connect
|
33
|
+
end
|
34
|
+
|
35
|
+
def connect
|
36
|
+
@sock = TCPSocket.new(@address, @port)
|
37
|
+
@sock.gets
|
38
|
+
end
|
39
|
+
|
40
|
+
def get(what = 'help')
|
41
|
+
@sock.print "#{what.strip}\r\n"
|
42
|
+
@sock.flush
|
43
|
+
JSON.parse(@sock.gets.strip)
|
44
|
+
rescue Errno::EPIPE
|
45
|
+
self.connect
|
46
|
+
retry
|
47
|
+
rescue NoMethodError
|
48
|
+
nil
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
data/lib/arbi/config.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
#--
|
2
|
+
# Copyleft shura. [ shura1991@gmail.com ]
|
3
|
+
#
|
4
|
+
# This file is part of arbi.
|
5
|
+
#
|
6
|
+
# arbi is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU Affero General Public License as published
|
8
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# arbi is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU Affero General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU Affero General Public License
|
17
|
+
# along with arbi. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
#++
|
19
|
+
|
20
|
+
require 'yaml'
|
21
|
+
require 'singleton'
|
22
|
+
|
23
|
+
module Arbi
|
24
|
+
|
25
|
+
class Config < Hash
|
26
|
+
include Singleton
|
27
|
+
|
28
|
+
def initialize
|
29
|
+
super()
|
30
|
+
self.parse
|
31
|
+
end
|
32
|
+
|
33
|
+
def parse(file = '/etc/arbi.conf')
|
34
|
+
config = YAML.load_file(file)
|
35
|
+
rescue
|
36
|
+
Arbi.debug "Config: Error in parsing"
|
37
|
+
ensure
|
38
|
+
config ||= {}
|
39
|
+
config[:server] ||= {}
|
40
|
+
config[:server][:address] ||= '127.0.0.1'
|
41
|
+
config[:server][:port] ||= 6969
|
42
|
+
config[:client] ||= {}
|
43
|
+
config[:client][:address] ||= '127.0.0.1'
|
44
|
+
config[:client][:port] ||= 6969
|
45
|
+
config[:client][:default_cmd] ||= ['help', 'quit']
|
46
|
+
config[:modules] ||= {}
|
47
|
+
config[:modules][:path] ||= []
|
48
|
+
config[:modules][:modules] ||= []
|
49
|
+
|
50
|
+
config[:client][:default_cmd].map!(&:to_s).uniq!
|
51
|
+
config[:client][:default_cmd].delete('quit')
|
52
|
+
config[:client][:default_cmd] << 'quit'
|
53
|
+
config[:modules][:path] << File.join(File.dirname(__FILE__), 'modules')
|
54
|
+
config[:modules][:path].uniq!
|
55
|
+
|
56
|
+
self.replace(config)
|
57
|
+
end
|
58
|
+
|
59
|
+
class << self
|
60
|
+
def [](*args)
|
61
|
+
self.instance.[](*args)
|
62
|
+
end
|
63
|
+
|
64
|
+
def parse(*args)
|
65
|
+
self.instance.parse(*args)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
data/lib/arbi/modules.rb
ADDED
@@ -0,0 +1,183 @@
|
|
1
|
+
#--
|
2
|
+
# Copyleft shura. [ shura1991@gmail.com ]
|
3
|
+
#
|
4
|
+
# This file is part of arbi.
|
5
|
+
#
|
6
|
+
# arbi is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU Affero General Public License as published
|
8
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# arbi is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU Affero General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU Affero General Public License
|
17
|
+
# along with arbi. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
#++
|
19
|
+
|
20
|
+
require 'json'
|
21
|
+
require 'singleton'
|
22
|
+
require 'arbi/config'
|
23
|
+
require 'arbi/timeline'
|
24
|
+
require 'arbi/utils/table'
|
25
|
+
require 'arbi/utils/numeric'
|
26
|
+
|
27
|
+
module Arbi
|
28
|
+
|
29
|
+
module Modules
|
30
|
+
PATH = [File.join(File.dirname(__FILE__), 'modules')]
|
31
|
+
|
32
|
+
@@logger = STDERR
|
33
|
+
|
34
|
+
class Error
|
35
|
+
def initialize(data = "")
|
36
|
+
@data = data
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_json(*a)
|
40
|
+
({
|
41
|
+
'json_class' => self.class.name,
|
42
|
+
'data' => @data
|
43
|
+
}.to_json(*a)) + "\n"
|
44
|
+
end
|
45
|
+
|
46
|
+
def format
|
47
|
+
@data
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.json_create(o)
|
51
|
+
self.new(o['data'])
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class Module
|
56
|
+
attr_reader :data
|
57
|
+
|
58
|
+
def initialize(data = [])
|
59
|
+
@data = data
|
60
|
+
end
|
61
|
+
|
62
|
+
def every(ev = 2, args = {}, &blk)
|
63
|
+
args[:timeout] ||= 5
|
64
|
+
TimeLine::Job.new(ev, args[:timeout], &blk)
|
65
|
+
end
|
66
|
+
|
67
|
+
def to_json(*a)
|
68
|
+
self.refresh if self.respond_to?(:refresh)
|
69
|
+
|
70
|
+
{
|
71
|
+
'json_class' => self.class.to_s,
|
72
|
+
'data' => @data
|
73
|
+
}.to_json(*a) + "\n"
|
74
|
+
end
|
75
|
+
|
76
|
+
def format
|
77
|
+
"#{self.class.name} has no format specified, writing raw:\n#{data.to_json}"
|
78
|
+
end
|
79
|
+
|
80
|
+
def valid?
|
81
|
+
false
|
82
|
+
end
|
83
|
+
|
84
|
+
class << self
|
85
|
+
def json_create(o)
|
86
|
+
self.new(o['data'])
|
87
|
+
end
|
88
|
+
|
89
|
+
def inherited(obj)
|
90
|
+
@@modules ||= {}
|
91
|
+
Singleton.__init__(obj)
|
92
|
+
Arbi::Modules.uninit
|
93
|
+
@@modules[obj.name] = obj
|
94
|
+
end
|
95
|
+
|
96
|
+
def modules
|
97
|
+
@@modules
|
98
|
+
end
|
99
|
+
|
100
|
+
def name
|
101
|
+
self.to_s.downcase.split('::').last
|
102
|
+
end
|
103
|
+
|
104
|
+
alias __method_missing__ method_missing
|
105
|
+
def method_missing(sym, *args, &blk)
|
106
|
+
if self.instance.respond_to?(sym)
|
107
|
+
self.instance.send(sym, *args, &blk)
|
108
|
+
else
|
109
|
+
self.__method_missing__(sym, *args, &blk)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
undef_method :to_json
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
class << self
|
118
|
+
@@init = false
|
119
|
+
|
120
|
+
def modules
|
121
|
+
Arbi::Modules::Module.modules.dup
|
122
|
+
end
|
123
|
+
|
124
|
+
def init
|
125
|
+
return if self.initialized?
|
126
|
+
Arbi::Modules::Module.modules.replace(
|
127
|
+
Hash[Arbi::Modules::Module.modules.to_a.select {|(key, value)|
|
128
|
+
value.valid?
|
129
|
+
}])
|
130
|
+
@@init = true
|
131
|
+
end
|
132
|
+
|
133
|
+
def uninit
|
134
|
+
@@init = false
|
135
|
+
end
|
136
|
+
|
137
|
+
def initialized?
|
138
|
+
@@init
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def self.debug(str)
|
144
|
+
@@logger.puts(str.to_s)
|
145
|
+
rescue
|
146
|
+
STDERR.puts(str.to_s)
|
147
|
+
end
|
148
|
+
|
149
|
+
def self.init
|
150
|
+
Arbi::Modules.init
|
151
|
+
end
|
152
|
+
|
153
|
+
def self.modules
|
154
|
+
Arbi.init
|
155
|
+
Arbi::Modules.modules
|
156
|
+
end
|
157
|
+
|
158
|
+
end
|
159
|
+
|
160
|
+
def load_module(file)
|
161
|
+
lp = $:.dup
|
162
|
+
$:.replace(Arbi::Config[:modules][:path] + $:)
|
163
|
+
require file
|
164
|
+
rescue Exception => e
|
165
|
+
Arbi.debug("Error loading module: #{e}")
|
166
|
+
ensure
|
167
|
+
$:.replace(lp)
|
168
|
+
end
|
169
|
+
|
170
|
+
load_module('help')
|
171
|
+
load_module('version')
|
172
|
+
load_module('cpu')
|
173
|
+
load_module('ram')
|
174
|
+
load_module('net')
|
175
|
+
load_module('adapter')
|
176
|
+
load_module('battery')
|
177
|
+
load_module('diskstat')
|
178
|
+
|
179
|
+
Arbi::Config[:modules][:modules].each {|mod|
|
180
|
+
load_module(mod)
|
181
|
+
}
|
182
|
+
|
183
|
+
Arbi.init
|
@@ -0,0 +1,62 @@
|
|
1
|
+
#--
|
2
|
+
# Copyleft shura. [ shura1991@gmail.com ]
|
3
|
+
#
|
4
|
+
# This file is part of arbi.
|
5
|
+
#
|
6
|
+
# arbi is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU Affero General Public License as published
|
8
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# arbi is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU Affero General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU Affero General Public License
|
17
|
+
# along with arbi. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
#++
|
19
|
+
|
20
|
+
require 'arbi/modules/acpi/utils'
|
21
|
+
|
22
|
+
module Arbi
|
23
|
+
|
24
|
+
module Modules
|
25
|
+
|
26
|
+
module Acpi
|
27
|
+
|
28
|
+
class Adapter < Arbi::Modules::Module
|
29
|
+
include Arbi::Modules::Acpi::Utils
|
30
|
+
|
31
|
+
def initialize(data = [])
|
32
|
+
super(data)
|
33
|
+
@adapters = Dir.glob('/proc/acpi/ac_adapter/*')
|
34
|
+
end
|
35
|
+
|
36
|
+
def valid?
|
37
|
+
!@adapters.empty?
|
38
|
+
end
|
39
|
+
|
40
|
+
def refresh
|
41
|
+
@data = []
|
42
|
+
|
43
|
+
@adapters.each {|adapter|
|
44
|
+
@data << {
|
45
|
+
name: File.basename(adapter),
|
46
|
+
state: (hashize("#{adapter}/state")[:state] == 'on-line' ? true : false)
|
47
|
+
}
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
def format
|
52
|
+
tablize([['NAME', 'STATE']] + @data.map {|adapter|
|
53
|
+
[adapter[:name] || adapter['name'], (adapter[:state] || adapter['state']) ? 'on' : 'off']
|
54
|
+
})
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
#--
|
2
|
+
# Copyleft shura. [ shura1991@gmail.com ]
|
3
|
+
#
|
4
|
+
# This file is part of arbi.
|
5
|
+
#
|
6
|
+
# arbi is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU Affero General Public License as published
|
8
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# arbi is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU Affero General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU Affero General Public License
|
17
|
+
# along with arbi. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
#++
|
19
|
+
|
20
|
+
require 'arbi/modules/acpi/utils'
|
21
|
+
|
22
|
+
module Arbi
|
23
|
+
|
24
|
+
module Modules
|
25
|
+
|
26
|
+
module Acpi
|
27
|
+
|
28
|
+
class Battery < Arbi::Modules::Module
|
29
|
+
include Arbi::Modules::Acpi::Utils
|
30
|
+
|
31
|
+
def initialize(data = [])
|
32
|
+
super(data)
|
33
|
+
@batteries = Dir.glob("/proc/acpi/battery/*")
|
34
|
+
end
|
35
|
+
|
36
|
+
def valid?
|
37
|
+
File.exist?('/proc/acpi/battery') and !@batteries.empty?
|
38
|
+
end
|
39
|
+
|
40
|
+
def refresh
|
41
|
+
@data = []
|
42
|
+
|
43
|
+
@batteries.each {|battery|
|
44
|
+
@data << self.battery_info(battery)
|
45
|
+
}
|
46
|
+
@data << self.average(@data)
|
47
|
+
end
|
48
|
+
|
49
|
+
def format
|
50
|
+
tablize([['NAME', 'PERCENT', 'STATE', 'SANITY']] + @data.map {|x|
|
51
|
+
[x[:name] || x['name'], x[:percent] || x['percent'], x[:state] || x['state'], x[:sanity] || x['sanity']]
|
52
|
+
})
|
53
|
+
end
|
54
|
+
|
55
|
+
protected
|
56
|
+
def battery_info(dir)
|
57
|
+
raw = self.hashize(dir + '/info').merge(self.hashize(dir + '/state'))
|
58
|
+
# Return if battery isn't present
|
59
|
+
return not_present(File.basename(dir)) unless {yes: true, no: false}[(raw[:present].to_sym rescue nil)]
|
60
|
+
|
61
|
+
{
|
62
|
+
name: File.basename(dir),
|
63
|
+
sanity: ("%.1f%%" % [100.0 / raw[:design_capacity] * raw[:last_full_capacity]]),
|
64
|
+
state: raw[:charging_state],
|
65
|
+
percent: ("%.1f%%" % [100.0 / raw[:last_full_capacity] * raw[:remaining_capacity]])
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
def average(batteries)
|
70
|
+
avg, n = 0, 0
|
71
|
+
|
72
|
+
batteries.each {|battery|
|
73
|
+
avg += battery[:percent].gsub(/%$/, '').to_f and n += 1 if battery[:percent]
|
74
|
+
}
|
75
|
+
avg = "%.1f%%" % [avg / n] rescue false
|
76
|
+
|
77
|
+
{
|
78
|
+
name: 'AVERAGE',
|
79
|
+
sanity: false,
|
80
|
+
state: false,
|
81
|
+
percent: avg
|
82
|
+
}
|
83
|
+
end
|
84
|
+
|
85
|
+
def not_present(name)
|
86
|
+
{
|
87
|
+
name: name,
|
88
|
+
sanity: false,
|
89
|
+
state: false,
|
90
|
+
percent: false
|
91
|
+
}
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|