train 0.29.2 → 0.30.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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +26 -2
  3. data/lib/train.rb +1 -0
  4. data/lib/train/errors.rb +6 -0
  5. data/lib/train/extras.rb +0 -1
  6. data/lib/train/platforms.rb +82 -0
  7. data/lib/train/platforms/common.rb +34 -0
  8. data/lib/train/platforms/detect.rb +12 -0
  9. data/lib/train/platforms/detect/helpers/os_common.rb +56 -0
  10. data/lib/train/platforms/detect/helpers/os_linux.rb +75 -0
  11. data/lib/train/{extras/os_detect_windows.rb → platforms/detect/helpers/os_windows.rb} +3 -10
  12. data/lib/train/platforms/detect/scanner.rb +84 -0
  13. data/lib/train/platforms/detect/specifications/os.rb +480 -0
  14. data/lib/train/platforms/family.rb +26 -0
  15. data/lib/train/platforms/platform.rb +80 -0
  16. data/lib/train/plugins/base_connection.rb +75 -27
  17. data/lib/train/transports/docker.rb +17 -28
  18. data/lib/train/transports/local.rb +21 -22
  19. data/lib/train/transports/mock.rb +44 -30
  20. data/lib/train/transports/ssh_connection.rb +55 -67
  21. data/lib/train/transports/winrm_connection.rb +16 -26
  22. data/lib/train/version.rb +1 -1
  23. data/test/unit/file/remote/linux_test.rb +2 -2
  24. data/test/unit/platforms/detect/os_common_test.rb +85 -0
  25. data/test/unit/platforms/detect/os_linux_test.rb +124 -0
  26. data/test/unit/{extras/os_detect_windows_test.rb → platforms/detect/os_windows_test.rb} +5 -2
  27. data/test/unit/platforms/detect/scanner_test.rb +61 -0
  28. data/test/unit/platforms/family_test.rb +32 -0
  29. data/test/unit/platforms/os_detect_test.rb +175 -0
  30. data/test/unit/{extras/os_common_test.rb → platforms/platform_test.rb} +103 -18
  31. data/test/unit/platforms/platforms_test.rb +42 -0
  32. data/test/unit/plugins/connection_test.rb +106 -8
  33. data/test/unit/transports/local_test.rb +20 -15
  34. data/test/unit/transports/mock_test.rb +16 -6
  35. data/test/unit/transports/ssh_test.rb +17 -15
  36. metadata +28 -19
  37. data/lib/train/extras/linux_lsb.rb +0 -60
  38. data/lib/train/extras/os_common.rb +0 -151
  39. data/lib/train/extras/os_detect_arista_eos.rb +0 -34
  40. data/lib/train/extras/os_detect_darwin.rb +0 -40
  41. data/lib/train/extras/os_detect_esx.rb +0 -22
  42. data/lib/train/extras/os_detect_linux.rb +0 -164
  43. data/lib/train/extras/os_detect_openvms.rb +0 -29
  44. data/lib/train/extras/os_detect_unix.rb +0 -106
  45. data/lib/train/extras/uname.rb +0 -28
  46. data/lib/train/transports/local_os.rb +0 -51
  47. data/test/unit/extras/os_detect_linux_test.rb +0 -230
@@ -0,0 +1,480 @@
1
+ # encoding: utf-8
2
+
3
+ # rubocop:disable Style/Next
4
+ # rubocop:disable Metrics/AbcSize
5
+ # rubocop:disable Metrics/CyclomaticComplexity
6
+ # rubocop:disable Metrics/ClassLength
7
+ # rubocop:disable Metrics/MethodLength
8
+ # rubocop:disable Metrics/PerceivedComplexity
9
+
10
+ module Train::Platforms::Detect::Specifications
11
+ class OS
12
+ def self.load
13
+ plat = Train::Platforms
14
+
15
+ plat.family('windows')
16
+ .detect {
17
+ if winrm? || (@backend.local? && ruby_host_os(/mswin|mingw32|windows/))
18
+ true
19
+ end
20
+ }
21
+ # windows platform
22
+ plat.name('windows').in_family('windows')
23
+ .detect {
24
+ true if detect_windows == true
25
+ }
26
+
27
+ # unix master family
28
+ plat.family('unix')
29
+ .detect {
30
+ if unix_uname_s =~ /./
31
+ @platform[:arch] = unix_uname_m
32
+ true
33
+ end
34
+ }
35
+
36
+ # arista_eos family
37
+ # this has to be before redhat as EOS is based off fedora
38
+ plat.family('arista_eos').title('Arista EOS Family').in_family('unix')
39
+ .detect {
40
+ # we need a better way to determin this family
41
+ # for now we are going to just try each platform
42
+ true
43
+ }
44
+ plat.name('arista_eos').title('Arista EOS').in_family('arista_eos')
45
+ .detect {
46
+ cmd = @backend.run_command('show version | json')
47
+ if cmd.exit_status == 0 && !cmd.stdout.empty?
48
+ require 'json'
49
+ eos_ver = JSON.parse(cmd.stdout)
50
+ @platform[:release] = eos_ver['version']
51
+ @platform[:arch] = eos_ver['architecture']
52
+ true
53
+ end
54
+ }
55
+ plat.name('arista_eos_bash').title('Arista EOS Bash Shell').in_family('arista_eos')
56
+ .detect {
57
+ if unix_file_exist?('/usr/bin/FastCli')
58
+ cmd = @backend.run_command('FastCli -p 15 -c "show version | json"')
59
+ if cmd.exit_status == 0 && !cmd.stdout.empty?
60
+ require 'json'
61
+ eos_ver = JSON.parse(cmd.stdout)
62
+ @platform[:release] = eos_ver['version']
63
+ @platform[:arch] = eos_ver['architecture']
64
+ true
65
+ end
66
+ end
67
+ }
68
+
69
+ # linux master family
70
+ plat.family('linux').in_family('unix')
71
+ .detect {
72
+ true if unix_uname_s =~ /linux/i
73
+ }
74
+
75
+ # debian family
76
+ plat.family('debian').in_family('linux')
77
+ .detect {
78
+ true unless unix_file_contents('/etc/debian_version').nil?
79
+ }
80
+ plat.name('ubuntu').title('Ubuntu Linux').in_family('debian')
81
+ .detect {
82
+ lsb = read_linux_lsb
83
+ if lsb && lsb[:id] =~ /ubuntu/i
84
+ @platform[:release] = lsb[:release]
85
+ true
86
+ end
87
+ }
88
+ plat.name('linuxmint').title('LinuxMint').in_family('debian')
89
+ .detect {
90
+ lsb = read_linux_lsb
91
+ if lsb && lsb[:id] =~ /linuxmint/i
92
+ @platform[:release] = lsb[:release]
93
+ true
94
+ end
95
+ }
96
+ plat.name('raspbian').title('Raspbian Linux').in_family('debian')
97
+ .detect {
98
+ if (linux_os_release && linux_os_release['NAME'] =~ /raspbian/i) || \
99
+ unix_file_exist?('/usr/bin/raspi-config')
100
+ @platform[:release] = unix_file_contents('/etc/debian_version').chomp
101
+ true
102
+ end
103
+ }
104
+ plat.name('debian').title('Debian Linux').in_family('debian')
105
+ .detect {
106
+ lsb = read_linux_lsb
107
+ if lsb && lsb[:id] =~ /debian/i
108
+ @platform[:release] = lsb[:release]
109
+ true
110
+ end
111
+
112
+ # if we get this far we have to be some type of debian
113
+ true
114
+ }
115
+
116
+ # fedora family
117
+ plat.family('fedora').in_family('linux')
118
+ .detect {
119
+ true if linux_os_release && linux_os_release['NAME'] =~ /fedora/i
120
+ }
121
+ plat.name('fedora').title('Fedora').in_family('fedora')
122
+ .detect {
123
+ @platform[:release] = linux_os_release['VERSION_ID']
124
+ true
125
+ }
126
+
127
+ # redhat family
128
+ plat.family('redhat').in_family('linux')
129
+ .detect {
130
+ # I am not sure this returns true for all redhats in this family
131
+ # for now we are going to just try each platform
132
+ # return true unless unix_file_contents('/etc/redhat-release').nil?
133
+
134
+ true
135
+ }
136
+ plat.name('centos').title('Centos Linux').in_family('redhat')
137
+ .detect {
138
+ lsb = read_linux_lsb
139
+ if lsb && lsb[:id] =~ /centos/i
140
+ @platform[:release] = lsb[:release]
141
+ true
142
+ elsif linux_os_release && linux_os_release['NAME'] =~ /centos/i
143
+ @platform[:release] = redhatish_version(unix_file_contents('/etc/redhat-release'))
144
+ true
145
+ end
146
+ }
147
+ plat.name('oracle').title('Oracle Linux').in_family('redhat')
148
+ .detect {
149
+ if !(raw = unix_file_contents('/etc/oracle-release')).nil?
150
+ @platform[:release] = redhatish_version(raw)
151
+ true
152
+ elsif !(raw = unix_file_contents('/etc/enterprise-release')).nil?
153
+ @platform[:release] = redhatish_version(raw)
154
+ true
155
+ end
156
+ }
157
+ plat.name('scientific').title('Scientific Linux').in_family('redhat')
158
+ .detect {
159
+ lsb = read_linux_lsb
160
+ if lsb && lsb[:id] =~ /scientificsl/i
161
+ @platform[:release] = lsb[:release]
162
+ true
163
+ end
164
+ }
165
+ plat.name('xenserver').title('Xenserer Linux').in_family('redhat')
166
+ .detect {
167
+ lsb = read_linux_lsb
168
+ if lsb && lsb[:id] =~ /xenserver/i
169
+ @platform[:release] = lsb[:release]
170
+ true
171
+ end
172
+ }
173
+ plat.name('parallels-release').title('Parallels Linux').in_family('redhat')
174
+ .detect {
175
+ if !(raw = unix_file_contents('/etc/parallels-release')).nil?
176
+ @platform[:name] = redhatish_platform(raw)
177
+ @platform[:release] = raw[/(\d\.\d\.\d)/, 1]
178
+ true
179
+ end
180
+ }
181
+ plat.name('wrlinux').title('Wind River Linux').in_family('redhat')
182
+ .detect {
183
+ if linux_os_release && linux_os_release['ID_LIKE'] =~ /wrlinux/i
184
+ @platform[:release] = linux_os_release['VERSION']
185
+ true
186
+ end
187
+ }
188
+ plat.name('amazon').title('Amazon Linux').in_family('redhat')
189
+ .detect {
190
+ lsb = read_linux_lsb
191
+ if lsb && lsb[:id] =~ /amazon/i
192
+ @platform[:release] = lsb[:release]
193
+ true
194
+ elsif (raw = unix_file_contents('/etc/system-release')) =~ /amazon/i
195
+ @platform[:name] = redhatish_platform(raw)
196
+ @platform[:release] = redhatish_version(raw)
197
+ true
198
+ end
199
+ }
200
+ # keep redhat at the end as a fallback for anything with a redhat-release
201
+ plat.name('redhat').title('Red Hat Linux').in_family('redhat')
202
+ .detect {
203
+ lsb = read_linux_lsb
204
+ if lsb && lsb[:id] =~ /redhat/i
205
+ @platform[:release] = lsb[:release]
206
+ true
207
+ elsif !(raw = unix_file_contents('/etc/redhat-release')).nil?
208
+ # must be some type of redhat
209
+ @platform[:name] = redhatish_platform(raw)
210
+ @platform[:release] = redhatish_version(raw)
211
+ true
212
+ end
213
+ }
214
+
215
+ # suse family
216
+ plat.family('suse').in_family('linux')
217
+ .detect {
218
+ if !(suse = unix_file_contents('/etc/SuSE-release')).nil?
219
+ version = suse.scan(/VERSION = (\d+)\nPATCHLEVEL = (\d+)/).flatten.join('.')
220
+ version = suse[/VERSION = ([\d\.]{2,})/, 1] if version == ''
221
+ @platform[:release] = version
222
+ true
223
+ end
224
+ }
225
+ plat.name('opensuse').title('OpenSUSE Linux').in_family('suse')
226
+ .detect {
227
+ true if unix_file_contents('/etc/SuSE-release') =~ /^opensuse/i
228
+ }
229
+ plat.name('suse').title('Suse Linux').in_family('suse')
230
+ .detect {
231
+ true if unix_file_contents('/etc/SuSE-release') =~ /suse/i
232
+ }
233
+
234
+ # arch
235
+ plat.name('arch').title('Arch Linux').in_family('linux')
236
+ .detect {
237
+ if !unix_file_contents('/etc/arch-release').nil?
238
+ # Because this is a rolling release distribution,
239
+ # use the kernel release, ex. 4.1.6-1-ARCH
240
+ @platform[:release] = unix_uname_r
241
+ true
242
+ end
243
+ }
244
+
245
+ # slackware
246
+ plat.name('slackware').title('Slackware Linux').in_family('linux')
247
+ .detect {
248
+ if !(raw = unix_file_contents('/etc/slackware-version')).nil?
249
+ @platform[:release] = raw.scan(/(\d+|\.+)/).join
250
+ true
251
+ end
252
+ }
253
+
254
+ # gentoo
255
+ plat.name('gentoo').title('Gentoo Linux').in_family('linux')
256
+ .detect {
257
+ if !(raw = unix_file_contents('/etc/gentoo-release')).nil?
258
+ @platform[:release] = raw.scan(/(\d+|\.+)/).join
259
+ true
260
+ end
261
+ }
262
+
263
+ # exherbo
264
+ plat.name('exherbo').title('Exherbo Linux').in_family('linux')
265
+ .detect {
266
+ unless unix_file_contents('/etc/exherbo-release').nil?
267
+ # Because this is a rolling release distribution,
268
+ # use the kernel release, ex. 4.1.6
269
+ @platform[:release] = unix_uname_r
270
+ true
271
+ end
272
+ }
273
+
274
+ # alpine
275
+ plat.name('alpine').title('Alpine Linux').in_family('linux')
276
+ .detect {
277
+ if !(raw = unix_file_contents('/etc/alpine-release')).nil?
278
+ @platform[:release] = raw.strip
279
+ true
280
+ end
281
+ }
282
+
283
+ # coreos
284
+ plat.name('coreos').title('CoreOS Linux').in_family('linux')
285
+ .detect {
286
+ unless unix_file_contents('/etc/coreos/update.conf').nil?
287
+ lsb = read_linux_lsb
288
+ @platform[:release] = lsb[:release]
289
+ true
290
+ end
291
+ }
292
+
293
+ # genaric linux
294
+ # this should always be last in the linux family list
295
+ plat.name('linux').title('Genaric Linux').in_family('linux')
296
+ .detect {
297
+ true
298
+ }
299
+
300
+ # openvms
301
+ plat.name('openvms').title('OpenVMS').in_family('unix')
302
+ .detect {
303
+ if unix_uname_s =~ /unrecognized command verb/i
304
+ cmd = @backend.run_command('show system/noprocess')
305
+ unless cmd.exit_status != 0 || cmd.stdout.empty?
306
+ @platform[:name] = cmd.stdout.downcase.split(' ')[0]
307
+ cmd = @backend.run_command('write sys$output f$getsyi("VERSION")')
308
+ @platform[:release] = cmd.stdout.downcase.split("\n")[1][1..-1]
309
+ cmd = @backend.run_command('write sys$output f$getsyi("ARCH_NAME")')
310
+ @platform[:arch] = cmd.stdout.downcase.split("\n")[1]
311
+ true
312
+ end
313
+ end
314
+ }
315
+
316
+ # esx
317
+ plat.family('esx').title('ESXi Family')
318
+ .detect {
319
+ true if unix_uname_s =~ /vmkernel/i
320
+ }
321
+ plat.name('vmkernel').in_family('esx')
322
+ .detect {
323
+ @platform[:name] = unix_uname_s.lines[0].chomp
324
+ @platform[:release] = unix_uname_r.lines[0].chomp
325
+ true
326
+ }
327
+
328
+ # aix
329
+ plat.family('aix').in_family('unix')
330
+ .detect {
331
+ true if unix_uname_s =~ /aix/i
332
+ }
333
+ plat.name('aix').title('Aix').in_family('aix')
334
+ .detect {
335
+ out = @backend.run_command('uname -rvp').stdout
336
+ m = out.match(/(\d+)\s+(\d+)\s+(.*)/)
337
+ unless m.nil?
338
+ @platform[:release] = "#{m[2]}.#{m[1]}"
339
+ @platform[:arch] = m[3].to_s
340
+ end
341
+ true
342
+ }
343
+
344
+ # solaris family
345
+ plat.family('solaris').in_family('unix')
346
+ .detect {
347
+ if unix_uname_s =~ /sunos/i
348
+ unless (version = /^5\.(?<release>\d+)$/.match(unix_uname_r)).nil?
349
+ @platform[:release] = version['release']
350
+ end
351
+
352
+ arch = @backend.run_command('uname -p')
353
+ @platform[:arch] = arch.stdout.chomp if arch.exit_status == 0
354
+ true
355
+ end
356
+ }
357
+ plat.name('smartos').title('SmartOS').in_family('solaris')
358
+ .detect {
359
+ rel = unix_file_contents('/etc/release')
360
+ if /^.*(SmartOS).*$/ =~ rel
361
+ true
362
+ end
363
+ }
364
+ plat.name('omnios').title('Omnios').in_family('solaris')
365
+ .detect {
366
+ rel = unix_file_contents('/etc/release')
367
+ if !(m = /^\s*(OmniOS).*r(\d+).*$/.match(rel)).nil?
368
+ @platform[:release] = m[2]
369
+ true
370
+ end
371
+ }
372
+ plat.name('openindiana').title('Openindiana').in_family('solaris')
373
+ .detect {
374
+ rel = unix_file_contents('/etc/release')
375
+ if !(m = /^\s*(OpenIndiana).*oi_(\d+).*$/.match(rel)).nil?
376
+ @platform[:release] = m[2]
377
+ true
378
+ end
379
+ }
380
+ plat.name('opensolaris').title('Open Solaris').in_family('solaris')
381
+ .detect {
382
+ rel = unix_file_contents('/etc/release')
383
+ if !(m = /^\s*(OpenSolaris).*snv_(\d+).*$/.match(rel)).nil?
384
+ @platform[:release] = m[2]
385
+ true
386
+ end
387
+ }
388
+ plat.name('nexentacore').title('Nexentacore').in_family('solaris')
389
+ .detect {
390
+ rel = unix_file_contents('/etc/release')
391
+ if /^\s*(NexentaCore)\s.*$/ =~ rel
392
+ true
393
+ end
394
+ }
395
+ plat.name('solaris').title('Solaris').in_family('solaris')
396
+ .detect {
397
+ rel = unix_file_contents('/etc/release')
398
+ if !(m = /Oracle Solaris (\d+)/.match(rel)).nil?
399
+ # TODO: should be string!
400
+ @platform[:release] = m[1]
401
+ true
402
+ elsif /^\s*(Solaris)\s.*$/ =~ rel
403
+ true
404
+ end
405
+
406
+ # must be some unknown solaris
407
+ true
408
+ }
409
+
410
+ # hpux
411
+ plat.family('hpux').in_family('unix')
412
+ .detect {
413
+ true if unix_uname_s =~ /hp-ux/i
414
+ }
415
+ plat.name('hpux').title('Hpux').in_family('hpux')
416
+ .detect {
417
+ @platform[:release] = unix_uname_r.lines[0].chomp
418
+ true
419
+ }
420
+
421
+ # bsd family
422
+ plat.family('bsd').in_family('unix')
423
+ .detect {
424
+ # we need a better way to determin this family
425
+ # for now we are going to just try each platform
426
+ true
427
+ }
428
+ plat.family('darwin').in_family('bsd')
429
+ .detect {
430
+ cmd = unix_file_contents('/usr/bin/sw_vers')
431
+ if unix_uname_s =~ /darwin/i || !cmd.nil?
432
+ unless cmd.nil?
433
+ m = cmd.match(/^ProductVersion:\s+(.+)$/)
434
+ @platform[:release] = m.nil? ? nil : m[1]
435
+ m = cmd.match(/^BuildVersion:\s+(.+)$/)
436
+ @platform[:build] = m.nil? ? nil : m[1]
437
+ end
438
+ @platform[:release] = unix_uname_r.lines[0].chomp if @platform[:release].nil?
439
+ @platform[:arch] = unix_uname_m
440
+ true
441
+ end
442
+ }
443
+ plat.name('mac_os_x').title('macOS X').in_family('darwin')
444
+ .detect {
445
+ cmd = unix_file_contents('/System/Library/CoreServices/SystemVersion.plist')
446
+ true if cmd =~ /Mac OS X/i
447
+ }
448
+ plat.name('darwin').title('Darwin').in_family('darwin')
449
+ .detect {
450
+ # must be some other type of darwin
451
+ @platform[:name] = unix_uname_s.lines[0].chomp
452
+ true
453
+ }
454
+ plat.name('freebsd').title('Freebsd').in_family('bsd')
455
+ .detect {
456
+ if unix_uname_s =~ /freebsd/i
457
+ @platform[:name] = unix_uname_s.lines[0].chomp
458
+ @platform[:release] = unix_uname_r.lines[0].chomp
459
+ true
460
+ end
461
+ }
462
+ plat.name('openbsd').title('Openbsd').in_family('bsd')
463
+ .detect {
464
+ if unix_uname_s =~ /openbsd/i
465
+ @platform[:name] = unix_uname_s.lines[0].chomp
466
+ @platform[:release] = unix_uname_r.lines[0].chomp
467
+ true
468
+ end
469
+ }
470
+ plat.name('netbsd').title('Netbsd').in_family('bsd')
471
+ .detect {
472
+ if unix_uname_s =~ /netbsd/i
473
+ @platform[:name] = unix_uname_s.lines[0].chomp
474
+ @platform[:release] = unix_uname_r.lines[0].chomp
475
+ true
476
+ end
477
+ }
478
+ end
479
+ end
480
+ end