kanrisuru 0.1.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +17 -0
  3. data/.rubocop.yml +47 -0
  4. data/.rubocop_todo.yml +0 -0
  5. data/CHANGELOG.md +0 -0
  6. data/Gemfile +2 -5
  7. data/LICENSE.txt +1 -1
  8. data/README.md +143 -7
  9. data/Rakefile +5 -3
  10. data/bin/console +4 -3
  11. data/kanrisuru.gemspec +21 -12
  12. data/lib/kanrisuru.rb +41 -2
  13. data/lib/kanrisuru/command.rb +99 -0
  14. data/lib/kanrisuru/core.rb +53 -0
  15. data/lib/kanrisuru/core/archive.rb +154 -0
  16. data/lib/kanrisuru/core/disk.rb +302 -0
  17. data/lib/kanrisuru/core/file.rb +332 -0
  18. data/lib/kanrisuru/core/find.rb +108 -0
  19. data/lib/kanrisuru/core/group.rb +97 -0
  20. data/lib/kanrisuru/core/ip.rb +1032 -0
  21. data/lib/kanrisuru/core/mount.rb +138 -0
  22. data/lib/kanrisuru/core/path.rb +140 -0
  23. data/lib/kanrisuru/core/socket.rb +168 -0
  24. data/lib/kanrisuru/core/stat.rb +104 -0
  25. data/lib/kanrisuru/core/stream.rb +121 -0
  26. data/lib/kanrisuru/core/system.rb +348 -0
  27. data/lib/kanrisuru/core/transfer.rb +203 -0
  28. data/lib/kanrisuru/core/user.rb +198 -0
  29. data/lib/kanrisuru/logger.rb +8 -0
  30. data/lib/kanrisuru/mode.rb +277 -0
  31. data/lib/kanrisuru/os_package.rb +235 -0
  32. data/lib/kanrisuru/remote.rb +10 -0
  33. data/lib/kanrisuru/remote/cluster.rb +95 -0
  34. data/lib/kanrisuru/remote/cpu.rb +68 -0
  35. data/lib/kanrisuru/remote/env.rb +33 -0
  36. data/lib/kanrisuru/remote/file.rb +354 -0
  37. data/lib/kanrisuru/remote/fstab.rb +412 -0
  38. data/lib/kanrisuru/remote/host.rb +191 -0
  39. data/lib/kanrisuru/remote/memory.rb +19 -0
  40. data/lib/kanrisuru/remote/os.rb +87 -0
  41. data/lib/kanrisuru/result.rb +78 -0
  42. data/lib/kanrisuru/template.rb +32 -0
  43. data/lib/kanrisuru/util.rb +40 -0
  44. data/lib/kanrisuru/util/bits.rb +203 -0
  45. data/lib/kanrisuru/util/fs_mount_opts.rb +655 -0
  46. data/lib/kanrisuru/util/os_family.rb +213 -0
  47. data/lib/kanrisuru/util/signal.rb +161 -0
  48. data/lib/kanrisuru/version.rb +3 -1
  49. data/spec/functional/core/archive_spec.rb +228 -0
  50. data/spec/functional/core/disk_spec.rb +80 -0
  51. data/spec/functional/core/file_spec.rb +341 -0
  52. data/spec/functional/core/find_spec.rb +52 -0
  53. data/spec/functional/core/group_spec.rb +65 -0
  54. data/spec/functional/core/ip_spec.rb +71 -0
  55. data/spec/functional/core/path_spec.rb +93 -0
  56. data/spec/functional/core/socket_spec.rb +31 -0
  57. data/spec/functional/core/stat_spec.rb +98 -0
  58. data/spec/functional/core/stream_spec.rb +99 -0
  59. data/spec/functional/core/system_spec.rb +96 -0
  60. data/spec/functional/core/transfer_spec.rb +108 -0
  61. data/spec/functional/core/user_spec.rb +76 -0
  62. data/spec/functional/os_package_spec.rb +75 -0
  63. data/spec/functional/remote/cluster_spec.rb +45 -0
  64. data/spec/functional/remote/cpu_spec.rb +41 -0
  65. data/spec/functional/remote/env_spec.rb +36 -0
  66. data/spec/functional/remote/fstab_spec.rb +76 -0
  67. data/spec/functional/remote/host_spec.rb +68 -0
  68. data/spec/functional/remote/memory_spec.rb +29 -0
  69. data/spec/functional/remote/os_spec.rb +63 -0
  70. data/spec/functional/remote/remote_file_spec.rb +180 -0
  71. data/spec/helper/test_hosts.rb +68 -0
  72. data/spec/hosts.json +92 -0
  73. data/spec/spec_helper.rb +11 -3
  74. data/spec/unit/fstab_spec.rb +22 -0
  75. data/spec/unit/kanrisuru_spec.rb +9 -0
  76. data/spec/unit/mode_spec.rb +183 -0
  77. data/spec/unit/template_spec.rb +13 -0
  78. data/spec/unit/util_spec.rb +177 -0
  79. data/spec/zz_reboot_spec.rb +46 -0
  80. metadata +136 -13
  81. data/spec/kanrisuru_spec.rb +0 -9
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kanrisuru
4
+ module Core
5
+ module Stream
6
+ extend OsPackage::Define
7
+
8
+ os_define :linux, :head
9
+ os_define :linux, :tail
10
+ os_define :linux, :read_file_chunk
11
+ os_define :linux, :sed
12
+ os_define :linux, :echo
13
+ os_define :linux, :cat
14
+
15
+ def head(path, opts = {})
16
+ command = Kanrisuru::Command.new('head')
17
+ command.append_arg('-c', opts[:bytes])
18
+ command.append_arg('-n', opts[:lines])
19
+ command << path
20
+
21
+ execute_shell(command)
22
+
23
+ Kanrisuru::Result.new(command, &:to_a)
24
+ end
25
+
26
+ def tail(path, opts = {})
27
+ command = Kanrisuru::Command.new('tail')
28
+ command.append_arg('-c', opts[:bytes])
29
+ command.append_arg('-n', opts[:lines])
30
+ command << path
31
+
32
+ execute_shell(command)
33
+
34
+ Kanrisuru::Result.new(command, &:to_a)
35
+ end
36
+
37
+ def read_file_chunk(path, start_line, end_line)
38
+ if start_line.instance_of?(Integer) || end_line.instance_of?(Integer) ||
39
+ start_line > end_line || start_line.negative?
40
+ raise ArgumentError, 'Invalid start or end'
41
+ end
42
+
43
+ end_line = end_line - start_line + 1
44
+ command = Kanrisuru::Command.new("tail -n +#{start_line} #{path} | head -n #{end_line}")
45
+
46
+ execute_shell(command)
47
+
48
+ Kanrisuru::Result.new(command, &:to_a)
49
+ end
50
+
51
+ def sed(path, expression, replacement, opts = {})
52
+ command = Kanrisuru::Command.new('sed')
53
+ command.append_flag('-i', opts[:in_place])
54
+ command.append_flag('-r', opts[:regexp_extended])
55
+
56
+ command << "'s/#{expression}/#{replacement}/g'"
57
+ command << "'#{path}'"
58
+
59
+ append_file(command, opts)
60
+ execute_shell(command)
61
+
62
+ Kanrisuru::Result.new(command) do |cmd|
63
+ if Kanrisuru::Util.present?(opts[:new_file])
64
+ nil
65
+ else
66
+ cmd.to_a.join("\n")
67
+ end
68
+ end
69
+ end
70
+
71
+ def echo(text, opts = {})
72
+ command = Kanrisuru::Command.new('echo')
73
+ command.append_flag('-e', opts[:backslash])
74
+ command << "'#{text}'"
75
+
76
+ append_file(command, opts)
77
+ execute_shell(command)
78
+
79
+ Kanrisuru::Result.new(command) do |cmd|
80
+ if Kanrisuru::Util.present?(opts[:new_file])
81
+ nil
82
+ else
83
+ cmd.to_s
84
+ end
85
+ end
86
+ end
87
+
88
+ def cat(files, opts = {})
89
+ command = Kanrisuru::Command.new('cat')
90
+ command.append_flag('-T', opts[:show_tabs])
91
+ command.append_flag('-n', opts[:number])
92
+ command.append_flag('-s', opts[:squeeze_blank])
93
+ command.append_flag('-v', opts[:show_nonprinting])
94
+ command.append_flag('-E', opts[:show_ends])
95
+ command.append_flag('-b', opts[:number_nonblank])
96
+ command.append_flag('-A', opts[:show_all])
97
+
98
+ files = files.instance_of?(String) ? [files] : files
99
+ command << files.join(' ')
100
+
101
+ append_file(command, opts)
102
+ execute_shell(command)
103
+
104
+ Kanrisuru::Result.new(command, &:to_a)
105
+ end
106
+
107
+ private
108
+
109
+ def append_file(command, opts)
110
+ return unless Kanrisuru::Util.present?(opts[:new_file])
111
+
112
+ case opts[:mode]
113
+ when 'append'
114
+ command << ">> #{opts[:new_file]}"
115
+ when 'write'
116
+ command << "> #{opts[:new_file]}"
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,348 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'date'
4
+
5
+ module Kanrisuru
6
+ module Core
7
+ module System
8
+ extend OsPackage::Define
9
+
10
+ os_define :linux, :load_env
11
+ os_define :linux, :cpu_info
12
+ os_define :linux, :load_average
13
+ os_define :linux, :free
14
+ os_define :linux, :ps
15
+ os_define :linux, :kill
16
+
17
+ os_define :linux, :uptime
18
+ os_define :linux, :w
19
+ os_define :linux, :who
20
+
21
+ os_define :linux, :reboot
22
+ os_define :linux, :poweroff
23
+
24
+ ProcessInfo = Struct.new(
25
+ :uid, :user, :gid, :group, :ppid, :pid,
26
+ :cpu_usage, :memory_usage, :stat, :priority,
27
+ :flags, :policy_abbr, :policy, :cpu_time, :command
28
+ )
29
+
30
+ Uptime = Struct.new(
31
+ :boot_time, :uptime, :seconds, :minutes, :hours, :days
32
+ )
33
+
34
+ UserLoggedIn = Struct.new(:user, :tty, :ip, :login, :idle, :jcpu, :pcpu, :command)
35
+
36
+ def load_env
37
+ command = Kanrisuru::Command.new('env')
38
+ execute_shell(command)
39
+
40
+ Kanrisuru::Result.new(command) do |cmd|
41
+ string = cmd.to_s
42
+ hash = {}
43
+
44
+ rows = string.split("\n")
45
+ rows.each do |row|
46
+ key, value = row.split('=', 2)
47
+ hash[key] = value
48
+ end
49
+
50
+ hash
51
+ end
52
+ end
53
+
54
+ def cpu_info(spec)
55
+ name =
56
+ case spec
57
+ when 'sockets'
58
+ '^Socket'
59
+ when 'cores_per_socket'
60
+ '^Core'
61
+ when 'threads'
62
+ '^Thread'
63
+ when 'cores'
64
+ '^CPU('
65
+ else
66
+ return
67
+ end
68
+
69
+ command = Kanrisuru::Command.new("lscpu | grep -i '#{name}' | awk '{print $NF}'")
70
+ execute(command)
71
+
72
+ Kanrisuru::Result.new(command, &:to_i)
73
+ end
74
+
75
+ def load_average
76
+ command = Kanrisuru::Command.new("cat /proc/loadavg | awk '{print $1,$2,$3}'")
77
+ execute(command)
78
+
79
+ Kanrisuru::Result.new(command) do |cmd|
80
+ cmd.to_s.split.map(&:to_f)
81
+ end
82
+ end
83
+
84
+ def free(type)
85
+ conversions = {
86
+ total: 'MemTotal',
87
+ free: 'MemFree',
88
+ swap: 'SwapTotal',
89
+ swap_free: 'SwapFree'
90
+ }
91
+
92
+ option = conversions[type.to_sym]
93
+ raise ArgumentError, 'Invalid mem type' unless option
94
+
95
+ command = Kanrisuru::Command.new("cat /proc/meminfo | grep -i '^#{option}' | awk '{print $2}'")
96
+ execute(command)
97
+
98
+ ## In kB
99
+ Kanrisuru::Result.new(command, &:to_i)
100
+ end
101
+
102
+ def ps(opts = {})
103
+ group = opts[:group]
104
+ user = opts[:user]
105
+ pid = opts[:pid]
106
+ ppid = opts[:ppid]
107
+ # tree = opts[:tree]
108
+
109
+ command = Kanrisuru::Command.new('ps ww')
110
+
111
+ if !user.nil? || !group.nil? || !pid.nil? || !ppid.nil?
112
+ command.append_arg('--user', user.instance_of?(Array) ? user.join(',') : user)
113
+ command.append_arg('--group', group.instance_of?(Array) ? group.join(',') : group)
114
+ command.append_arg('--pid', pid.instance_of?(Array) ? pid.join(',') : pid)
115
+ command.append_arg('--ppid', ppid.instance_of?(Array) ? ppid.join(',') : ppid)
116
+ else
117
+ command.append_flag('ax')
118
+ end
119
+
120
+ command.append_arg('-o', 'uid,user,gid,group,ppid,pid,pcpu,pmem,stat,pri,flags,policy,time,cmd')
121
+ command.append_flag('--no-headers')
122
+
123
+ execute(command)
124
+
125
+ ## Have found issues with regular newline parsing from command
126
+ ## most likely a buffer overflow from SSH buffer.
127
+ ## Join string then split by newline.
128
+ Kanrisuru::Result.new(command) do |cmd|
129
+ result_string = cmd.raw_result.join
130
+ rows = result_string.split("\n")
131
+
132
+ build_process_list(rows)
133
+ end
134
+ end
135
+
136
+ def kill(signal, pids)
137
+ raise ArgumentError, 'Invalid signal' unless Kanrisuru::Util::Signal.valid?(signal)
138
+
139
+ ## Use named signals for readabilitiy
140
+ signal = Kanrisuru::Util::Signal[signal] if signal.instance_of?(Integer)
141
+
142
+ command = Kanrisuru::Command.new('kill')
143
+ command << "-#{signal}"
144
+ command << (pids.instance_of?(Array) ? pids.join(' ') : pids)
145
+
146
+ execute_shell(command)
147
+
148
+ Kanrisuru::Result.new(command)
149
+ end
150
+
151
+ def uptime
152
+ ## Ported from https://github.com/djberg96/sys-uptime
153
+ command = Kanrisuru::Command.new('cat /proc/uptime')
154
+
155
+ execute_shell(command)
156
+
157
+ Kanrisuru::Result.new(command) do |cmd|
158
+ seconds = cmd.to_s.split[0].to_i
159
+ minutes = seconds / 60
160
+ hours = seconds / 3600
161
+ days = seconds / 86_400
162
+
163
+ seconds_dur = seconds
164
+ days_dur = seconds_dur / 86_400
165
+ seconds_dur -= days_dur * 86_400
166
+ hours_dur = seconds_dur / 3600
167
+ seconds_dur -= hours_dur * 3600
168
+ minutes_dur = seconds_dur / 60
169
+ seconds_dur -= minutes_dur * 60
170
+ uptime_s = "#{days_dur}:#{hours_dur}:#{minutes_dur}:#{seconds_dur}"
171
+
172
+ boot_time = Time.now - seconds
173
+
174
+ Uptime.new(
175
+ boot_time,
176
+ uptime_s,
177
+ seconds,
178
+ minutes,
179
+ hours,
180
+ days
181
+ )
182
+ end
183
+ end
184
+
185
+ def w(opts = {})
186
+ users = opts[:users]
187
+ command = Kanrisuru::Command.new('w -h')
188
+
189
+ command << users if Kanrisuru::Util.present?(users)
190
+
191
+ execute(command)
192
+
193
+ Kanrisuru::Result.new(command) do |cmd|
194
+ result_string = cmd.raw_result.join
195
+ rows = result_string.split("\n")
196
+
197
+ rows.map do |row|
198
+ values = *row.split(/\s+/, 8)
199
+ UserLoggedIn.new(
200
+ values[0],
201
+ values[1],
202
+ IPAddr.new(values[2]),
203
+ values[3],
204
+ values[4],
205
+ values[5].to_f,
206
+ values[6].to_f,
207
+ values[7]
208
+ )
209
+ end
210
+ end
211
+ end
212
+
213
+ def who(opts = {})
214
+ w(opts)
215
+ end
216
+
217
+ def reboot(opts = {})
218
+ time = opts[:time]
219
+ cancel = opts[:cancel]
220
+ message = opts[:message]
221
+ no_wall = opts[:no_wall]
222
+
223
+ command = Kanrisuru::Command.new('shutdown')
224
+
225
+ if Kanrisuru::Util.present?(cancel)
226
+ command.append_flag('-c')
227
+ command << message if message
228
+ else
229
+ command.append_flag('-r')
230
+
231
+ time = format_shutdown_time(time) if Kanrisuru::Util.present?(time)
232
+
233
+ if Kanrisuru::Util.present?(no_wall)
234
+ command.append_flag('--no-wall')
235
+ else
236
+ command << time if time
237
+ command << message if message
238
+ end
239
+ end
240
+
241
+ begin
242
+ execute_shell(command)
243
+ Kanrisuru::Result.new(command)
244
+ rescue IOError
245
+ ## When rebooting 'now', ssh io stream closes
246
+ ## Set exit status to 0
247
+ command.handle_status(0)
248
+ Kanrisuru::Result.new(command)
249
+ end
250
+ end
251
+
252
+ def poweroff(opts = {})
253
+ time = opts[:time]
254
+ cancel = opts[:cancel]
255
+ message = opts[:message]
256
+ no_wall = opts[:no_wall]
257
+
258
+ command = Kanrisuru::Command.new('shutdown')
259
+
260
+ if Kanrisuru::Util.present?(cancel)
261
+ command.append_flag('-c')
262
+ command << message if message
263
+ else
264
+ time = format_shutdown_time(time) if Kanrisuru::Util.present?(time)
265
+
266
+ if Kanrisuru::Util.present?(no_wall)
267
+ command.append_flag('--no-wall')
268
+ else
269
+ command << time if time
270
+ command << message if message
271
+ end
272
+ end
273
+
274
+ begin
275
+ execute_shell(command)
276
+
277
+ Kanrisuru::Result.new(command)
278
+ rescue IOError
279
+ ## When powering off 'now', ssh io stream closes
280
+ ## Set exit status to 0
281
+ command.handle_status(0)
282
+
283
+ Kanrisuru::Result.new(command)
284
+ end
285
+ end
286
+
287
+ private
288
+
289
+ def parse_policy_abbr(value)
290
+ case value
291
+ when '-'
292
+ 'not reported'
293
+ when 'TS'
294
+ 'SCHED_OTHER'
295
+ when 'FF'
296
+ 'SCHED_FIFO'
297
+ when 'RR'
298
+ 'SCHED_RR'
299
+ when 'B'
300
+ 'SCHED_BATCH'
301
+ when 'ISO'
302
+ 'SCHED_ISO'
303
+ when 'IDL'
304
+ 'SCHED_IDLE'
305
+ when 'DLN'
306
+ 'SCHED_DEADLINE'
307
+ else
308
+ 'unknown value'
309
+ end
310
+ end
311
+
312
+ def build_process_list(rows)
313
+ rows.map do |row|
314
+ values = *row.split(/\s+/, 15)
315
+ values.shift if values[0] == ''
316
+
317
+ ProcessInfo.new(
318
+ values[0].to_i,
319
+ values[1],
320
+ values[2].to_i,
321
+ values[3],
322
+ values[4].to_i,
323
+ values[5].to_i,
324
+ values[6].to_f,
325
+ values[7].to_f,
326
+ values[8],
327
+ values[9].to_i,
328
+ values[10].to_i,
329
+ values[11],
330
+ parse_policy_abbr(values[11]),
331
+ values[12],
332
+ values[13]
333
+ )
334
+ end
335
+ end
336
+
337
+ def format_shutdown_time(time)
338
+ if time.instance_of?(Integer) || !/^[0-9]+$/.match(time).nil?
339
+ "+#{time}"
340
+ elsif !/^[0-9]{0,2}:[0-9]{0,2}$/.match(time).nil? || time == 'now'
341
+ time
342
+ else
343
+ raise ArgumentError, 'Invalid time format'
344
+ end
345
+ end
346
+ end
347
+ end
348
+ end