miab 0.2.0 → 0.4.0

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.
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