miab 0.2.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/lib/miab.rb +314 -129
  5. metadata +6 -26
  6. metadata.gz.sig +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f55a97e81e82e74208daba1ab739870e2d20a69a24f1399ecc26d311fd7758b5
4
- data.tar.gz: 9365c7761a511133a1489f48d8f925d969972735fcd5204b439c047f85fa852a
3
+ metadata.gz: a339ac7a434f6d8f59cfacb59a5f70e56c4cbe490712a9ec70bec7992cae9036
4
+ data.tar.gz: f44e1bf9f71fe2fc250841c9b69ae4eb62a69176bff014bc8293f23c17b6cf35
5
5
  SHA512:
6
- metadata.gz: fc1f8615bb288650541853d281a6096217f2d5960fcb6de3d509012fec1fef88b70095dc8fb81c7aa2e53db2b4920f901318f2e95e28c6949e1513e9388029b3
7
- data.tar.gz: a85242c85b472ca36301579a8439cf6557a0cda61bc8709256624d66d842e218a0a6f661cf6339090e26356aea4704c7cff4cd7420812216285be7d0f155fb76
6
+ metadata.gz: 42d047bc15e959ff24b194063547065875d495f23ccd09f5e7d0f607f326f6a5f9aeef0894c9f90d5459bcc64453d1abdfe5d8031bb8e5389e68181343ef525c
7
+ data.tar.gz: c390aebd8577fcbba6569549a69358c673bd85e44ac1b1f5f49fcd904885af78084a0aa76b478796adfcf7a7097af48514b8d5a48ba5f9283473b0994914d3ed
Binary file
data.tar.gz.sig CHANGED
Binary file
@@ -4,188 +4,373 @@
4
4
 
5
5
  # Desc: Message in a bottle (MIAB) is designed to execute remote commands
6
6
  # through SSH for system maintenance purposes.
7
+ #
8
+ # Note: Intended for a Debian based distro
7
9
 
8
10
  require 'net/ssh'
9
11
  require 'c32'
10
- require 'resolve/hostname'
11
-
12
+ require 'resolv'
13
+
14
+
15
+ # available commands:
16
+ #
17
+ # * backup - initiates rsync on the remote machine to be backed up
18
+ # * date - returns the system date and time
19
+ # * directory_exists? - returns true if the directory exists
20
+ # * disk_space - returns the available disk space
21
+ # * echo - returns whatever string is passed in
22
+ # * file_exists? - returns true if the file exists
23
+ # * file_write - writes contents to a file
24
+ # * installable? - returns true if the package name exists in apt-cache search
25
+ # * installed? - returns true if a package is already installed
26
+ # * internet? - returns true if a ping request to an external IP address succeeds
27
+ # * memory - returns the amount of free RAM available etc
28
+ # * ping - returns the latency of a ping request to the node
29
+ # * pwd - returns the working directory
30
+ # * temperature - returns the temperature of the CPU
31
+
32
+ # usage: puts Miab.new("temperature", domain: 'home', target: 'angelo',
33
+ # user: 'pi', password: 'secret').cast
34
+ # nearest SSH equivalent `ssh pi@angelo.home exec \
35
+ # "cat /sys/class/thermal/thermal_zone0/temp"`
12
36
 
13
37
  class Miab
14
38
  using ColouredText
39
+
40
+ class Session
41
+
42
+ def initialize( host, ssh=nil, debug: false, dns: '1.1.1.1')
43
+
44
+ @ssh, @host, @debug, @dns = ssh, host, debug, dns
45
+
46
+ puts 'Session dns: ' + dns.inspect if @debug
47
+ @results = {}
48
+ end
49
+
50
+ def exec(s)
51
+ eval s
52
+ @results
53
+ end
54
+
55
+ protected
56
+
57
+ # backup
58
+ # e.g. from: /mnt/usbdisk/gem_src/.
59
+ # to: pi@192.168.4.158:backup2020/gem_src/.
60
+ #
61
+ def backup(from: nil, to: nil)
62
+
63
+ if @debug then
64
+ puts 'ready to perform backup'
65
+ puts "from: %s to: %s" % [from, to]
66
+ end
67
+
68
+ instructions = "rsync -akL -e ssh %s %s" % [from, to]
69
+
70
+ puts 'instructions: ' + instructions if @debug
71
+
72
+ # note: compression is not enabled since this is aimed at
73
+ # single board computers which have limited CPU capability
74
+
75
+ r = @ssh ? @ssh.exec!(instructions) : `#{instructions}`
76
+ puts 'r: ' + r.inspect if @debug
77
+
78
+ # since it's running in the background, an empty string will be returned
79
+
80
+ end
81
+
82
+ # return the local date and time
83
+ #
84
+ def date()
15
85
 
16
- def initialize(scroll, domain: nil, target: nil, pwlist: {}, password: nil,
17
- user: nil, debug: false)
86
+ instructions = 'date'
87
+ r = @ssh ? @ssh.exec!(instructions) : `#{instructions}`
88
+ puts 'r: ' + r.inspect if @debug
89
+ @results[:date] = r.chomp
18
90
 
19
- @results = {}
91
+ end
92
+
93
+ def directory_exists?(file)
94
+
95
+ instructions = "test -d #{file}; echo $?"
96
+ r = @ssh ? @ssh.exec!(instructions) : `#{instructions}`
97
+ puts 'r: ' + r.inspect if @debug
98
+
99
+ @results[:directory_exists?] = r.chomp == '0'
20
100
 
21
- @scroll, @debug = scroll, debug
101
+ end
102
+
103
+ alias dir_exists? directory_exists?
22
104
 
23
- @nodes = if target then
105
+ # query the available disk space etc.
106
+ #
107
+ def disk_space()
24
108
 
25
- target = [target] if target.is_a? String
109
+ instructions = 'df -h'
110
+ r = @ssh ? @ssh.exec!(instructions) : `#{instructions}`
26
111
 
27
- target.inject({}) do |r,x|
28
- host = domain ? x + '.' + domain : x
29
- passwd = pwlist[x] || password
30
- userhost = user ? user + '@' + host : host
31
- r.merge({userhost => passwd})
32
- end
112
+ @results[:disk_usage] = {}
33
113
 
34
- else
35
- {}
36
- end
37
- end
114
+ a = r.lines.grep(/\/dev\/root/)
38
115
 
39
- # cast out the thing and hope for the best
40
- #
41
- def cast()
116
+ puts ('a: ' + a.inspect).debug if @debug
42
117
 
43
- if @nodes.any? then
118
+ if a.any? then
119
+ size, used, avail = a[0].split(/ +/).values_at(1,2,3)
44
120
 
45
- @nodes.each do |raw_host, password|
121
+ @results[:disk_usage][:root] = {size: size, used: used,
122
+ avail: avail}
123
+ end
46
124
 
47
- host, user = raw_host.split(/@/,2).reverse
48
- @host = host
49
- @results[host] = {}
50
-
51
- begin
52
- @ssh = Net::SSH.start( host, user, password: password)
53
- eval @scroll
54
- @ssh.close
55
- rescue
56
- @results[host] = nil
57
- end
58
-
125
+ a2 = r.lines.grep(/\/dev\/sda1/)
126
+
127
+ puts ('a2: ' + a2.inspect).debug if @debug
128
+
129
+ if a2.any? then
130
+ size, used, avail = a2[0].split(/ +/).values_at(1,2,3)
131
+
132
+ @results[:disk_usage][:sda1] = {size: size, used: used,
133
+ avail: avail}
59
134
  end
60
135
 
61
- else
62
- @results[`hostname`.chomp] = {}
63
- eval @scroll if @scroll
64
136
  end
65
137
 
66
- @scroll = nil
67
- @results
68
- end
138
+ alias df disk_space
139
+
140
+ # return the string supplied
141
+ #
142
+ def echo(s)
69
143
 
70
- # return the local date and time
71
- #
72
- def date()
144
+ instructions = 'echo ' + s
145
+ r = @ssh ? @ssh.exec!(instructions) : `#{instructions}`
146
+ puts 'r: ' + r.inspect if @debug
147
+ @results[:echo] = r.chomp
73
148
 
74
- instructions = 'date'
75
- r = @ssh ? @ssh.exec!(instructions) : system(instructions)
76
- @results[@host][:date] = r.chomp
149
+ end
150
+
151
+ def exec_success?(instruction, expected)
152
+
153
+ r = @ssh ? @ssh.exec!(instruction) : `#{instruction}`
154
+ puts 'r: ' + r.inspect if @debug
155
+
156
+ @results[:exec_success?] = (r =~ /#{expected}/ ? true : r)
157
+
158
+ end
159
+
160
+ def file_exists?(file)
161
+
162
+ instructions = "test -f #{file}; echo $?"
163
+ r = @ssh ? @ssh.exec!(instructions) : `#{instructions}`
164
+ puts 'r: ' + r.inspect if @debug
165
+
166
+ @results[:file_exists?] = r == 0
167
+
168
+ end
169
+
170
+ # e.g. file_write 'desc.txt', 'Controls the door entry system.'
171
+ #
172
+ def file_write(file, content)
173
+
174
+ instructions = "echo #{content.inspect} >> #{file}"
175
+ r = @ssh ? @ssh.exec!(instructions) : `#{instructions}`
176
+ puts 'r: ' + r.inspect if @debug
177
+
178
+ @results[:file_write] = r
179
+
180
+ end
181
+
182
+ # return the string supplied
183
+ #
184
+ def install(package)
185
+
186
+ return @results[:install] = 'no route to internet' unless internet?
187
+ return @results[:install] = 'already installed' if installed? package
188
+
189
+ instructions = "apt-get update && apt-get install #{package} -y"
190
+ r = @ssh ? @ssh.exec!(instructions) : `#{instructions}`
191
+ puts 'r: ' + r.inspect if @debug
192
+ @results[:install] = r.chomp
193
+
194
+ end
195
+
196
+ def installable?(package)
197
+
198
+ instructions = "apt-cache search --names-only ^#{package}$"
199
+ results = @ssh ? @ssh.exec!(instructions) : `#{instructions}`
200
+ puts 'results: ' + results.inspect if @debug
201
+
202
+ @results[:installable?] = !results.empty?
203
+
204
+ end
205
+
206
+ def installed?(package)
207
+
208
+ instructions = 'dpkg --get-selections | grep -i ' + package
209
+ results = @ssh ? @ssh.exec!(instructions) : `#{instructions}`
210
+ puts 'results: ' + results.inspect if @debug
211
+
212
+ return @results[:installed?] = nil if results.empty?
213
+ r = results.lines.grep /^#{package}/
214
+
215
+ @results[:installed?] = r.any?
216
+ end
77
217
 
78
- end
218
+ def internet?()
219
+
220
+ instructions = "ping #{@dns} -W 1 -c 1"
221
+ r = @ssh ? @ssh.exec!(instructions) : `#{instructions}`
222
+ puts 'r: ' + r.inspect if @debug
223
+
224
+ @results[:internet?] = r.lines[1][/icmp_seq/] ? true : false
79
225
 
80
- # query the available disk space etc.
81
- #
82
- def disk_space()
226
+ end
83
227
 
84
- instructions = 'df -h'
85
- s = @ssh ? @ssh.exec!(instructions) : system(instructions)
228
+ # find out available memory etc
229
+ #
230
+ def memory()
86
231
 
87
- @results[@host][:disk_usage] = {}
232
+ instructions = 'free -h'
88
233
 
89
- a = s.lines.grep(/\/dev\/root/)
234
+ puts ('instructions: ' + instructions.inspect).debug if @debug
235
+ r = @ssh ? @ssh.exec!(instructions) : `#{instructions}`
236
+ puts ('memory: ' + r.inspect).debug if @debug
237
+ a = r.lines
238
+ total, used, avail = a[1].split.values_at(1,2,-1)
239
+ @results[:memory] = {total: total, used: used, available: avail}
90
240
 
91
- puts ('a: ' + a.inspect).debug if @debug
241
+ end
242
+
243
+ # query the ping time
244
+ #
245
+ def ping()
246
+
247
+ ip = Resolv.getaddress(@host)
248
+ puts ('ip: ' + ip.inspect).debug if @debug
249
+ valid = pingecho(ip)
250
+ puts ('valid: ' + valid.inspect).debug if @debug
251
+
252
+ @results[:ping] = if valid then
253
+ a = [valid]
254
+ 4.times {sleep 0.01; a << pingecho(ip)}
255
+ (a.min * 1000).round(3)
256
+ else
257
+ nil
258
+ end
92
259
 
93
- if a.any? then
94
- size, used, avail = a[0].split(/ +/).values_at(1,2,3)
260
+ end
95
261
 
96
- @results[@host][:disk_usage][:root] = {size: size, used: used,
97
- avail: avail}
262
+ # query the path of the current working directory
263
+ #
264
+ def pwd()
265
+ instructions = 'pwd'
266
+ r = @ssh ? @ssh.exec!(instructions) : `#{instructions}`
267
+ @results[:pwd] = r.chomp
98
268
  end
99
269
 
100
- a2 = s.lines.grep(/\/dev\/sda1/)
270
+ # query the CPU temperature
271
+ #
272
+ def temperature()
273
+ instructions = 'cat /sys/class/thermal/thermal_zone0/temp'
274
+ r = @ssh ? @ssh.exec!(instructions) : `#{instructions}`
275
+ @results[:temperature] = r.chomp
276
+ end
277
+
278
+ private
279
+
280
+
281
+ def pingecho(host, timeout=5, service="echo")
101
282
 
102
- puts ('a2: ' + a2.inspect).debug if @debug
283
+ elapsed = nil
284
+ time = Time.new
103
285
 
104
- if a2.any? then
105
- size, used, avail = a2[0].split(/ +/).values_at(1,2,3)
286
+ begin
106
287
 
107
- @results[@host][:disk_usage][:sda1] = {size: size, used: used,
108
- avail: avail}
109
- end
288
+ Timeout.timeout(timeout) do
289
+ s = TCPSocket.new(host, service)
290
+ s.close
291
+ end
110
292
 
293
+ rescue Errno::ECONNREFUSED
294
+ return Time.now - time
295
+ rescue Timeout::Error, StandardError
296
+ return false
297
+ end
298
+
299
+ # it should not reach this far
300
+ return true
301
+ end
302
+
303
+
111
304
  end
112
305
 
113
- alias df disk_space
306
+ def initialize(scroll, domain: nil, target: nil, pwlist: {}, password: nil,
307
+ user: nil, debug: false, dns: '208.67.222.222')
308
+
309
+ @results = {}
114
310
 
115
- # find out available memory etc
116
- #
117
- def memory()
311
+ @scroll, @debug, @dns = scroll, debug, dns
312
+
313
+ puts '@dns: ' + @dns.inspect if @debug
314
+
315
+ target = [target] if target.is_a? String
118
316
 
119
- instructions = 'free -h'
317
+ @nodes = if target then
120
318
 
121
- puts ('instructions: ' + instructions.inspect).debug if @debug
122
- r = @ssh ? @ssh.exec!(instructions) : system(instructions)
123
- puts ('memory: ' + r.inspect).debug if @debug
124
- a = r.lines
125
- total, used, avail = a[1].split.values_at(1,2,-1)
126
- @results[@host][:memory] = {total: total, used: used, available: avail}
319
+ target = [target] if target.is_a? String
320
+
321
+ target.inject({}) do |r,x|
322
+ host = domain ? x + '.' + domain : x
323
+ passwd = pwlist[x] || password
324
+ userhost = user ? user + '@' + host : host
325
+ r.merge({userhost => passwd})
326
+ end
127
327
 
128
- end
129
-
130
- # query the ping time
131
- #
132
- def ping()
133
-
134
- resolver = Resolve::Hostname.new
135
- ip = resolver.getaddress(@host)
136
- puts ('ip: ' + ip.inspect).debug if @debug
137
- valid = pingecho(ip)
138
- puts ('valid: ' + valid.inspect).debug if @debug
139
-
140
- @results[@host][:ping] = if valid then
141
- a = [valid]
142
- 4.times {sleep 0.01; a << pingecho(ip)}
143
- (a.min * 1000).round(3)
144
328
  else
145
- nil
329
+ {}
146
330
  end
147
-
148
331
  end
149
332
 
150
- # query the path of the current working directory
333
+ # cast out the thing and hope for the best
151
334
  #
152
- def pwd()
153
- instructions = 'pwd'
154
- r = @ssh ? @ssh.exec!(instructions) : system(instructions)
155
- @results[@host][:pwd] = r.chomp
156
- end
335
+ def cast()
157
336
 
158
- # query the CPU temperature
159
- #
160
- def temperature()
161
- instructions = 'cat /sys/class/thermal/thermal_zone0/temp'
162
- r = @ssh ? @ssh.exec!(instructions) : system(instructions)
163
- @results[@host][:temperature] = r.chomp
164
- end
165
-
166
- private
167
-
168
-
169
- def pingecho(host, timeout=5, service="echo")
337
+ if @nodes.any? then
338
+
339
+ threads = []
340
+ dns = @dns
170
341
 
171
- elapsed = nil
172
- time = Time.new
342
+ @nodes.each do |raw_host, password|
343
+
344
+ host, user = raw_host.split(/@/,2).reverse
345
+ @results[host] = {}
346
+
347
+ threads << Thread.new do
348
+ begin
349
+ puts ('host: ' + host.inspect).debug if @debug
350
+ ssh = Net::SSH.start( host, user, password: password)
351
+ @results[host] = Session.new(host, ssh, dns: dns, debug: @debug).exec @scroll
352
+ ssh.close
353
+ puts (host + ' result: ' + @results[host].inspect).debug if @debug
354
+ rescue
355
+ @results[host] = nil
356
+ end
357
+ end
358
+
359
+ end
173
360
 
174
- begin
361
+ threads.each(&:join)
175
362
 
176
- Timeout.timeout(timeout) do
177
- s = TCPSocket.new(host, service)
178
- s.close
363
+ else
364
+
365
+ if @scroll then
366
+ host = `hostname`.chomp
367
+ @results[host] = Session.new(host, dns: @dns, debug: @debug).exec(@scroll)
179
368
  end
180
-
181
- rescue Errno::ECONNREFUSED
182
- return Time.now - time
183
- rescue Timeout::Error, StandardError
184
- return false
369
+
185
370
  end
186
-
187
- # it should not reach this far
188
- return true
189
- end
371
+
372
+ @scroll = nil
373
+ @results
374
+ end
190
375
 
191
376
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: miab
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - James Robertson
@@ -35,7 +35,7 @@ cert_chain:
35
35
  9irKsAp/hZt3dTbQOtnSlc9XREZZdegwOgu1FEqBqviNIn9R28OR237HExiWXwmw
36
36
  HDTFIjk8XBqTunABuFUgr4qx
37
37
  -----END CERTIFICATE-----
38
- date: 2019-09-14 00:00:00.000000000 Z
38
+ date: 2020-08-27 00:00:00.000000000 Z
39
39
  dependencies:
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: net-ssh
@@ -43,20 +43,20 @@ dependencies:
43
43
  requirements:
44
44
  - - ">="
45
45
  - !ruby/object:Gem::Version
46
- version: 5.2.0
46
+ version: 6.1.0
47
47
  - - "~>"
48
48
  - !ruby/object:Gem::Version
49
- version: '5.2'
49
+ version: '6.1'
50
50
  type: :runtime
51
51
  prerelease: false
52
52
  version_requirements: !ruby/object:Gem::Requirement
53
53
  requirements:
54
54
  - - ">="
55
55
  - !ruby/object:Gem::Version
56
- version: 5.2.0
56
+ version: 6.1.0
57
57
  - - "~>"
58
58
  - !ruby/object:Gem::Version
59
- version: '5.2'
59
+ version: '6.1'
60
60
  - !ruby/object:Gem::Dependency
61
61
  name: c32
62
62
  requirement: !ruby/object:Gem::Requirement
@@ -77,26 +77,6 @@ dependencies:
77
77
  - - "~>"
78
78
  - !ruby/object:Gem::Version
79
79
  version: '0.2'
80
- - !ruby/object:Gem::Dependency
81
- name: resolve-hostname
82
- requirement: !ruby/object:Gem::Requirement
83
- requirements:
84
- - - ">="
85
- - !ruby/object:Gem::Version
86
- version: 0.1.0
87
- - - "~>"
88
- - !ruby/object:Gem::Version
89
- version: '0.1'
90
- type: :runtime
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - ">="
95
- - !ruby/object:Gem::Version
96
- version: 0.1.0
97
- - - "~>"
98
- - !ruby/object:Gem::Version
99
- version: '0.1'
100
80
  description:
101
81
  email: james@jamesrobertson.eu
102
82
  executables: []
metadata.gz.sig CHANGED
Binary file