train 1.1.1 → 1.2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +23 -2
- data/lib/train/platforms/detect/helpers/os_common.rb +17 -0
- data/lib/train/platforms/detect/specifications/os.rb +118 -82
- data/lib/train/plugins/transport.rb +1 -1
- data/lib/train/transports/local.rb +17 -5
- data/lib/train/transports/ssh.rb +1 -0
- data/lib/train/transports/ssh_connection.rb +7 -1
- data/lib/train/transports/winrm_connection.rb +1 -1
- data/lib/train/version.rb +1 -1
- data/test/unit/platforms/os_detect_test.rb +22 -0
- data/test/unit/plugins/connection_test.rb +2 -2
- data/test/unit/transports/ssh_test.rb +16 -0
- data/test/windows/local_test.rb +62 -6
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 124fd4ed4c208f576481fb0b295fb8db93d9dd21
|
|
4
|
+
data.tar.gz: '0680e4d022b89b9e94abcd2f76ec830c69936ec9'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7a82299c5aea2a11cbaf596a8aaa6bee503e7a169c86934b7af9a379ae70fc72fddbde527dcea1b717b08795d270a8092a223f72d7b582e8ded18b5c99180d56
|
|
7
|
+
data.tar.gz: db232796a60f74458d26ceff2273e72a233fcb14f794b03c6c9e45dfda7ba84e0632005a2c3c6df2ef9b2d7e816044f9589003cea87c890ea0e369fdc9f68838
|
data/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,31 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
-
## [1.
|
|
4
|
-
[Full Changelog](https://github.com/chef/train/compare/v1.1.
|
|
3
|
+
## [1.2.0](https://github.com/chef/train/tree/1.2.0) (2018-03-15)
|
|
4
|
+
[Full Changelog](https://github.com/chef/train/compare/v1.1.1...1.2.0)
|
|
5
|
+
|
|
6
|
+
**Implemented enhancements:**
|
|
7
|
+
|
|
8
|
+
- Change error message to use `connection` [\#263](https://github.com/chef/train/pull/263) ([jerryaldrichiii](https://github.com/jerryaldrichiii))
|
|
9
|
+
|
|
10
|
+
**Closed issues:**
|
|
11
|
+
|
|
12
|
+
- Force 64bit powershell if using ruby32 on a 64bit os [\#265](https://github.com/chef/train/issues/265)
|
|
13
|
+
- Master OS detect family [\#260](https://github.com/chef/train/issues/260)
|
|
14
|
+
|
|
15
|
+
**Merged pull requests:**
|
|
16
|
+
|
|
17
|
+
- Force 64bit powershell for 32bit ruby running on 64bit windows [\#266](https://github.com/chef/train/pull/266) ([jquick](https://github.com/jquick))
|
|
18
|
+
- support cisco ios xe [\#262](https://github.com/chef/train/pull/262) ([arlimus](https://github.com/arlimus))
|
|
19
|
+
- Create a master OS family and refactor specifications [\#261](https://github.com/chef/train/pull/261) ([jquick](https://github.com/jquick))
|
|
20
|
+
- Support for Brocade FOS-based SAN devices [\#254](https://github.com/chef/train/pull/254) ([marcelhuth](https://github.com/marcelhuth))
|
|
21
|
+
- ProxyCommand support [\#227](https://github.com/chef/train/pull/227) ([cbeckr](https://github.com/cbeckr))
|
|
22
|
+
|
|
23
|
+
## [v1.1.1](https://github.com/chef/train/tree/v1.1.1) (2018-02-14)
|
|
24
|
+
[Full Changelog](https://github.com/chef/train/compare/v1.1.0...v1.1.1)
|
|
5
25
|
|
|
6
26
|
**Merged pull requests:**
|
|
7
27
|
|
|
28
|
+
- Release train 1.1.1 [\#259](https://github.com/chef/train/pull/259) ([jquick](https://github.com/jquick))
|
|
8
29
|
- Add api sdk versions as platform release [\#258](https://github.com/chef/train/pull/258) ([jquick](https://github.com/jquick))
|
|
9
30
|
- Add plat helper methods to api direct platforms. [\#257](https://github.com/chef/train/pull/257) ([jquick](https://github.com/jquick))
|
|
10
31
|
|
|
@@ -53,6 +53,18 @@ module Train::Platforms::Detect::Helpers
|
|
|
53
53
|
@uname[:m] = command_output('uname -m')
|
|
54
54
|
end
|
|
55
55
|
|
|
56
|
+
def brocade_version
|
|
57
|
+
return @cache[:brocade] if @cache.key?(:brocade)
|
|
58
|
+
res = command_output('version')
|
|
59
|
+
|
|
60
|
+
m = res.match(/^Fabric OS:\s+v(\S+)$/)
|
|
61
|
+
unless m.nil?
|
|
62
|
+
return @cache[:brocade] = { version: m[1], type: 'fos' }
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
@cache[:brocade] = nil
|
|
66
|
+
end
|
|
67
|
+
|
|
56
68
|
def cisco_show_version
|
|
57
69
|
return @cache[:cisco] if @cache.key?(:cisco)
|
|
58
70
|
res = command_output('show version')
|
|
@@ -62,6 +74,11 @@ module Train::Platforms::Detect::Helpers
|
|
|
62
74
|
return @cache[:cisco] = { version: m[2], model: m[1], type: 'ios' }
|
|
63
75
|
end
|
|
64
76
|
|
|
77
|
+
m = res.match(/^Cisco IOS Software, IOS-XE Software, [^,]+? \(([^,]+?)\), Version (\d+\.\d+\.\d+[A-Z]*)/)
|
|
78
|
+
unless m.nil?
|
|
79
|
+
return @cache[:cisco] = { version: m[2], model: m[1], type: 'ios-xe' }
|
|
80
|
+
end
|
|
81
|
+
|
|
65
82
|
m = res.match(/^Cisco Nexus Operating System \(NX-OS\) Software/)
|
|
66
83
|
unless m.nil?
|
|
67
84
|
v = res[/^\s*system:\s+version (\d+\.\d+)/, 1]
|
|
@@ -12,7 +12,10 @@ module Train::Platforms::Detect::Specifications
|
|
|
12
12
|
def self.load
|
|
13
13
|
plat = Train::Platforms
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
# master family
|
|
16
|
+
plat.family('os').detect { true }
|
|
17
|
+
|
|
18
|
+
plat.family('windows').in_family('os')
|
|
16
19
|
.detect {
|
|
17
20
|
if winrm? || (@backend.local? && ruby_host_os(/mswin|mingw32|windows/))
|
|
18
21
|
true
|
|
@@ -25,81 +28,16 @@ module Train::Platforms::Detect::Specifications
|
|
|
25
28
|
}
|
|
26
29
|
|
|
27
30
|
# unix master family
|
|
28
|
-
plat.family('unix')
|
|
31
|
+
plat.family('unix').in_family('os')
|
|
29
32
|
.detect {
|
|
30
33
|
# we want to catch a special case here where cisco commands
|
|
31
34
|
# don't return an exit status and still print to stdout
|
|
32
|
-
if unix_uname_s =~ /./ && !unix_uname_s.start_with?('Line has invalid autocommand ')
|
|
35
|
+
if unix_uname_s =~ /./ && !unix_uname_s.start_with?('Line has invalid autocommand ') && !unix_uname_s.start_with?('The command you have entered')
|
|
33
36
|
@platform[:arch] = unix_uname_m
|
|
34
37
|
true
|
|
35
38
|
end
|
|
36
39
|
}
|
|
37
40
|
|
|
38
|
-
# cisco_ios family
|
|
39
|
-
plat.family('cisco').title('Cisco Family')
|
|
40
|
-
.detect {
|
|
41
|
-
!cisco_show_version.nil?
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
plat.name('cisco_ios').title('Cisco IOS').in_family('cisco')
|
|
45
|
-
.detect {
|
|
46
|
-
v = cisco_show_version
|
|
47
|
-
next unless v[:type] == 'ios'
|
|
48
|
-
@platform[:release] = v[:version]
|
|
49
|
-
@platform[:arch] = nil
|
|
50
|
-
true
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
plat.name('cisco_nexus').title('Cisco Nexus').in_family('cisco')
|
|
54
|
-
.detect {
|
|
55
|
-
v = cisco_show_version
|
|
56
|
-
next unless v[:type] == 'nexus'
|
|
57
|
-
@platform[:release] = v[:version]
|
|
58
|
-
@platform[:arch] = nil
|
|
59
|
-
true
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
# arista_eos family
|
|
63
|
-
# this has to be before redhat as EOS is based off fedora
|
|
64
|
-
plat.family('arista_eos').title('Arista EOS Family').in_family('unix')
|
|
65
|
-
.detect {
|
|
66
|
-
# we need a better way to determin this family
|
|
67
|
-
# for now we are going to just try each platform
|
|
68
|
-
true
|
|
69
|
-
}
|
|
70
|
-
plat.name('arista_eos').title('Arista EOS').in_family('arista_eos')
|
|
71
|
-
.detect {
|
|
72
|
-
cmd = @backend.run_command('show version | json')
|
|
73
|
-
if cmd.exit_status == 0 && !cmd.stdout.empty?
|
|
74
|
-
require 'json'
|
|
75
|
-
begin
|
|
76
|
-
eos_ver = JSON.parse(cmd.stdout)
|
|
77
|
-
@platform[:release] = eos_ver['version']
|
|
78
|
-
@platform[:arch] = eos_ver['architecture']
|
|
79
|
-
true
|
|
80
|
-
rescue JSON::ParserError
|
|
81
|
-
nil
|
|
82
|
-
end
|
|
83
|
-
end
|
|
84
|
-
}
|
|
85
|
-
plat.name('arista_eos_bash').title('Arista EOS Bash Shell').in_family('arista_eos')
|
|
86
|
-
.detect {
|
|
87
|
-
if unix_file_exist?('/usr/bin/FastCli')
|
|
88
|
-
cmd = @backend.run_command('FastCli -p 15 -c "show version | json"')
|
|
89
|
-
if cmd.exit_status == 0 && !cmd.stdout.empty?
|
|
90
|
-
require 'json'
|
|
91
|
-
begin
|
|
92
|
-
eos_ver = JSON.parse(cmd.stdout)
|
|
93
|
-
@platform[:release] = eos_ver['version']
|
|
94
|
-
@platform[:arch] = eos_ver['architecture']
|
|
95
|
-
true
|
|
96
|
-
rescue JSON::ParserError
|
|
97
|
-
nil
|
|
98
|
-
end
|
|
99
|
-
end
|
|
100
|
-
end
|
|
101
|
-
}
|
|
102
|
-
|
|
103
41
|
# linux master family
|
|
104
42
|
plat.family('linux').in_family('unix')
|
|
105
43
|
.detect {
|
|
@@ -159,6 +97,31 @@ module Train::Platforms::Detect::Specifications
|
|
|
159
97
|
true
|
|
160
98
|
}
|
|
161
99
|
|
|
100
|
+
# arista_eos family
|
|
101
|
+
# this checks for the arista bash shell
|
|
102
|
+
# must come before redhat as it uses fedora under the hood
|
|
103
|
+
plat.family('arista_eos').title('Arista EOS Family').in_family('linux')
|
|
104
|
+
.detect {
|
|
105
|
+
true
|
|
106
|
+
}
|
|
107
|
+
plat.name('arista_eos_bash').title('Arista EOS Bash Shell').in_family('arista_eos')
|
|
108
|
+
.detect {
|
|
109
|
+
if unix_file_exist?('/usr/bin/FastCli')
|
|
110
|
+
cmd = @backend.run_command('FastCli -p 15 -c "show version | json"')
|
|
111
|
+
if cmd.exit_status == 0 && !cmd.stdout.empty?
|
|
112
|
+
require 'json'
|
|
113
|
+
begin
|
|
114
|
+
eos_ver = JSON.parse(cmd.stdout)
|
|
115
|
+
@platform[:release] = eos_ver['version']
|
|
116
|
+
@platform[:arch] = eos_ver['architecture']
|
|
117
|
+
true
|
|
118
|
+
rescue JSON::ParserError
|
|
119
|
+
nil
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
}
|
|
124
|
+
|
|
162
125
|
# redhat family
|
|
163
126
|
plat.family('redhat').in_family('linux')
|
|
164
127
|
.detect {
|
|
@@ -325,6 +288,13 @@ module Train::Platforms::Detect::Specifications
|
|
|
325
288
|
end
|
|
326
289
|
}
|
|
327
290
|
|
|
291
|
+
# brocade family detected here if device responds to 'uname' command,
|
|
292
|
+
# happens when logging in as root
|
|
293
|
+
plat.family('brocade').title('Brocade Family').in_family('linux')
|
|
294
|
+
.detect {
|
|
295
|
+
!brocade_version.nil?
|
|
296
|
+
}
|
|
297
|
+
|
|
328
298
|
# genaric linux
|
|
329
299
|
# this should always be last in the linux family list
|
|
330
300
|
plat.name('linux').title('Genaric Linux').in_family('linux')
|
|
@@ -348,18 +318,6 @@ module Train::Platforms::Detect::Specifications
|
|
|
348
318
|
end
|
|
349
319
|
}
|
|
350
320
|
|
|
351
|
-
# esx
|
|
352
|
-
plat.family('esx').title('ESXi Family')
|
|
353
|
-
.detect {
|
|
354
|
-
true if unix_uname_s =~ /vmkernel/i
|
|
355
|
-
}
|
|
356
|
-
plat.name('vmkernel').in_family('esx')
|
|
357
|
-
.detect {
|
|
358
|
-
@platform[:name] = unix_uname_s.lines[0].chomp
|
|
359
|
-
@platform[:release] = unix_uname_r.lines[0].chomp
|
|
360
|
-
true
|
|
361
|
-
}
|
|
362
|
-
|
|
363
321
|
# aix
|
|
364
322
|
plat.family('aix').in_family('unix')
|
|
365
323
|
.detect {
|
|
@@ -475,8 +433,8 @@ module Train::Platforms::Detect::Specifications
|
|
|
475
433
|
}
|
|
476
434
|
plat.family('darwin').in_family('bsd')
|
|
477
435
|
.detect {
|
|
478
|
-
|
|
479
|
-
|
|
436
|
+
if unix_uname_s =~ /darwin/i
|
|
437
|
+
cmd = unix_file_contents('/usr/bin/sw_vers')
|
|
480
438
|
unless cmd.nil?
|
|
481
439
|
m = cmd.match(/^ProductVersion:\s+(.+)$/)
|
|
482
440
|
@platform[:release] = m.nil? ? nil : m[1]
|
|
@@ -523,6 +481,84 @@ module Train::Platforms::Detect::Specifications
|
|
|
523
481
|
true
|
|
524
482
|
end
|
|
525
483
|
}
|
|
484
|
+
|
|
485
|
+
# arista_eos family
|
|
486
|
+
plat.family('arista_eos').title('Arista EOS Family').in_family('os')
|
|
487
|
+
.detect {
|
|
488
|
+
true
|
|
489
|
+
}
|
|
490
|
+
plat.name('arista_eos').title('Arista EOS').in_family('arista_eos')
|
|
491
|
+
.detect {
|
|
492
|
+
cmd = @backend.run_command('show version | json')
|
|
493
|
+
if cmd.exit_status == 0 && !cmd.stdout.empty?
|
|
494
|
+
require 'json'
|
|
495
|
+
begin
|
|
496
|
+
eos_ver = JSON.parse(cmd.stdout)
|
|
497
|
+
@platform[:release] = eos_ver['version']
|
|
498
|
+
@platform[:arch] = eos_ver['architecture']
|
|
499
|
+
true
|
|
500
|
+
rescue JSON::ParserError
|
|
501
|
+
nil
|
|
502
|
+
end
|
|
503
|
+
end
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
# esx
|
|
507
|
+
plat.family('esx').title('ESXi Family').in_family('os')
|
|
508
|
+
.detect {
|
|
509
|
+
true if unix_uname_s =~ /vmkernel/i
|
|
510
|
+
}
|
|
511
|
+
plat.name('vmkernel').in_family('esx')
|
|
512
|
+
.detect {
|
|
513
|
+
@platform[:name] = unix_uname_s.lines[0].chomp
|
|
514
|
+
@platform[:release] = unix_uname_r.lines[0].chomp
|
|
515
|
+
true
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
# cisco_ios family
|
|
519
|
+
plat.family('cisco').title('Cisco Family').in_family('os')
|
|
520
|
+
.detect {
|
|
521
|
+
!cisco_show_version.nil?
|
|
522
|
+
}
|
|
523
|
+
plat.name('cisco_ios').title('Cisco IOS').in_family('cisco')
|
|
524
|
+
.detect {
|
|
525
|
+
v = cisco_show_version
|
|
526
|
+
next unless v[:type] == 'ios'
|
|
527
|
+
@platform[:release] = v[:version]
|
|
528
|
+
@platform[:arch] = nil
|
|
529
|
+
true
|
|
530
|
+
}
|
|
531
|
+
plat.name('cisco_ios_xe').title('Cisco IOS XE').in_family('cisco')
|
|
532
|
+
.detect {
|
|
533
|
+
v = cisco_show_version
|
|
534
|
+
next unless v[:type] == 'ios-xe'
|
|
535
|
+
@platform[:release] = v[:version]
|
|
536
|
+
@platform[:arch] = nil
|
|
537
|
+
true
|
|
538
|
+
}
|
|
539
|
+
plat.name('cisco_nexus').title('Cisco Nexus').in_family('cisco')
|
|
540
|
+
.detect {
|
|
541
|
+
v = cisco_show_version
|
|
542
|
+
next unless v[:type] == 'nexus'
|
|
543
|
+
@platform[:release] = v[:version]
|
|
544
|
+
@platform[:arch] = nil
|
|
545
|
+
true
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
# brocade family
|
|
549
|
+
plat.family('brocade').title('Brocade Family').in_family('os')
|
|
550
|
+
.detect {
|
|
551
|
+
!brocade_version.nil?
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
plat.name('brocade_fos').title('Brocade FOS').in_family('brocade')
|
|
555
|
+
.detect {
|
|
556
|
+
v = brocade_version
|
|
557
|
+
next unless v[:type] == 'fos'
|
|
558
|
+
@platform[:release] = v[:version]
|
|
559
|
+
@platform[:arch] = nil
|
|
560
|
+
true
|
|
561
|
+
}
|
|
526
562
|
end
|
|
527
563
|
end
|
|
528
564
|
end
|
|
@@ -30,7 +30,7 @@ class Train::Plugins
|
|
|
30
30
|
# @param [Hash] _options = nil provide optional configuration params
|
|
31
31
|
# @return [Connection] the connection for this configuration
|
|
32
32
|
def connection(_options = nil)
|
|
33
|
-
fail Train::ClientError, "#{self.class} does not implement #
|
|
33
|
+
fail Train::ClientError, "#{self.class} does not implement #connection()"
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
# Register the inheriting class with as a train plugin using the
|
|
@@ -43,11 +43,18 @@ module Train::Transports
|
|
|
43
43
|
|
|
44
44
|
def select_runner(options)
|
|
45
45
|
if os.windows?
|
|
46
|
+
# Force a 64 bit poweshell if needed
|
|
47
|
+
if RUBY_PLATFORM == 'i386-mingw32' && os.arch == 'x86_64'
|
|
48
|
+
powershell_cmd = "#{ENV['SystemRoot']}\\sysnative\\WindowsPowerShell\\v1.0\\powershell.exe"
|
|
49
|
+
else
|
|
50
|
+
powershell_cmd = 'powershell'
|
|
51
|
+
end
|
|
52
|
+
|
|
46
53
|
# Attempt to use a named pipe but fallback to ShellOut if that fails
|
|
47
54
|
begin
|
|
48
|
-
WindowsPipeRunner.new
|
|
55
|
+
WindowsPipeRunner.new(powershell_cmd)
|
|
49
56
|
rescue PipeError
|
|
50
|
-
WindowsShellRunner.new
|
|
57
|
+
WindowsShellRunner.new(powershell_cmd)
|
|
51
58
|
end
|
|
52
59
|
else
|
|
53
60
|
GenericRunner.new(self, options)
|
|
@@ -111,6 +118,10 @@ module Train::Transports
|
|
|
111
118
|
require 'json'
|
|
112
119
|
require 'base64'
|
|
113
120
|
|
|
121
|
+
def initialize(powershell_cmd = 'powershell')
|
|
122
|
+
@powershell_cmd = powershell_cmd
|
|
123
|
+
end
|
|
124
|
+
|
|
114
125
|
def run_command(script)
|
|
115
126
|
# Prevent progress stream from leaking into stderr
|
|
116
127
|
script = "$ProgressPreference='SilentlyContinue';" + script
|
|
@@ -119,7 +130,7 @@ module Train::Transports
|
|
|
119
130
|
script = script.encode('UTF-16LE', 'UTF-8')
|
|
120
131
|
base64_script = Base64.strict_encode64(script)
|
|
121
132
|
|
|
122
|
-
cmd = "
|
|
133
|
+
cmd = "#{@powershell_cmd} -NoProfile -EncodedCommand #{base64_script}"
|
|
123
134
|
|
|
124
135
|
res = Mixlib::ShellOut.new(cmd)
|
|
125
136
|
res.run_command
|
|
@@ -132,7 +143,8 @@ module Train::Transports
|
|
|
132
143
|
require 'base64'
|
|
133
144
|
require 'securerandom'
|
|
134
145
|
|
|
135
|
-
def initialize
|
|
146
|
+
def initialize(powershell_cmd = 'powershell')
|
|
147
|
+
@powershell_cmd = powershell_cmd
|
|
136
148
|
@pipe = acquire_pipe
|
|
137
149
|
fail PipeError if @pipe.nil?
|
|
138
150
|
end
|
|
@@ -206,7 +218,7 @@ module Train::Transports
|
|
|
206
218
|
|
|
207
219
|
utf8_script = script.encode('UTF-16LE', 'UTF-8')
|
|
208
220
|
base64_script = Base64.strict_encode64(utf8_script)
|
|
209
|
-
cmd = "
|
|
221
|
+
cmd = "#{@powershell_cmd} -NoProfile -ExecutionPolicy bypass -NonInteractive -EncodedCommand #{base64_script}"
|
|
210
222
|
|
|
211
223
|
server_pid = Process.create(command_line: cmd).process_id
|
|
212
224
|
|
data/lib/train/transports/ssh.rb
CHANGED
|
@@ -65,6 +65,7 @@ class Train::Transports::SSH
|
|
|
65
65
|
args += %w{ -o IdentitiesOnly=yes } if options[:keys]
|
|
66
66
|
args += %W( -o LogLevel=#{level} )
|
|
67
67
|
args += %W( -o ForwardAgent=#{fwd_agent} ) if options.key?(:forward_agent)
|
|
68
|
+
args += %W( -o ProxyCommand='#{options[:proxy_command]}' ) unless options[:proxy_command].nil?
|
|
68
69
|
Array(options[:keys]).each do |ssh_key|
|
|
69
70
|
args += %W( -i #{ssh_key} )
|
|
70
71
|
end
|
|
@@ -113,7 +114,7 @@ class Train::Transports::SSH
|
|
|
113
114
|
message: "Waiting for SSH service on #{@hostname}:#{@port}, " \
|
|
114
115
|
"retrying in #{delay} seconds",
|
|
115
116
|
)
|
|
116
|
-
|
|
117
|
+
run_command(PING_COMMAND.dup)
|
|
117
118
|
end
|
|
118
119
|
|
|
119
120
|
def uri
|
|
@@ -144,6 +145,11 @@ class Train::Transports::SSH
|
|
|
144
145
|
# @api private
|
|
145
146
|
def establish_connection(opts)
|
|
146
147
|
logger.debug("[SSH] opening connection to #{self}")
|
|
148
|
+
if @options[:proxy_command]
|
|
149
|
+
require 'net/ssh/proxy/command'
|
|
150
|
+
@options[:proxy] = Net::SSH::Proxy::Command.new(@options[:proxy_command])
|
|
151
|
+
@options.delete(:proxy_command)
|
|
152
|
+
end
|
|
147
153
|
Net::SSH.start(@hostname, @username, @options.clone.delete_if { |_key, value| value.nil? })
|
|
148
154
|
rescue *RESCUE_EXCEPTIONS_ON_ESTABLISH => e
|
|
149
155
|
if (opts[:retries] -= 1) <= 0
|
data/lib/train/version.rb
CHANGED
|
@@ -204,6 +204,16 @@ describe 'os_detect' do
|
|
|
204
204
|
platform[:release].must_equal('12.2')
|
|
205
205
|
end
|
|
206
206
|
|
|
207
|
+
it 'recognizes Cisco IOS XE' do
|
|
208
|
+
mock = Train::Transports::Mock::Connection.new
|
|
209
|
+
mock.mock_command('show version', "Cisco IOS Software, IOS-XE Software, Catalyst L3 Switch Software (CAT3K_CAA-UNIVERSALK9-M), Version 03.03.03SE RELEASE SOFTWARE (fc2)")
|
|
210
|
+
platform = Train::Platforms::Detect.scan(mock)
|
|
211
|
+
|
|
212
|
+
platform[:name].must_equal('cisco_ios_xe')
|
|
213
|
+
platform[:family].must_equal('cisco')
|
|
214
|
+
platform[:release].must_equal('03.03.03SE')
|
|
215
|
+
end
|
|
216
|
+
|
|
207
217
|
it 'recognizes Cisco Nexus' do
|
|
208
218
|
mock = Train::Transports::Mock::Connection.new
|
|
209
219
|
mock.mock_command('show version', "Cisco Nexus Operating System (NX-OS) Software\n system: version 5.2(1)N1(8b)\n")
|
|
@@ -214,4 +224,16 @@ describe 'os_detect' do
|
|
|
214
224
|
platform[:release].must_equal('5.2')
|
|
215
225
|
end
|
|
216
226
|
end
|
|
227
|
+
|
|
228
|
+
describe 'brocade' do
|
|
229
|
+
it 'recognizes Brocade FOS-based SAN switches' do
|
|
230
|
+
mock = Train::Transports::Mock::Connection.new
|
|
231
|
+
mock.mock_command('version', "Kernel: 2.6.14.2\nFabric OS: v7.4.2a\nMade on: Thu Jun 29 19:22:14 2017\nFlash: Sat Sep 9 17:30:42 2017\nBootProm: 1.0.11")
|
|
232
|
+
platform = Train::Platforms::Detect.scan(mock)
|
|
233
|
+
|
|
234
|
+
platform[:name].must_equal('brocade_fos')
|
|
235
|
+
platform[:family].must_equal('brocade')
|
|
236
|
+
platform[:release].must_equal('7.4.2a')
|
|
237
|
+
end
|
|
238
|
+
end
|
|
217
239
|
end
|
|
@@ -50,7 +50,7 @@ describe 'v1 Connection Plugin' do
|
|
|
50
50
|
plat.cloud?.must_equal false
|
|
51
51
|
plat.unix?.must_equal true
|
|
52
52
|
plat.family.must_equal 'darwin'
|
|
53
|
-
plat.family_hierarchy.must_equal ['darwin', 'bsd', 'unix']
|
|
53
|
+
plat.family_hierarchy.must_equal ['darwin', 'bsd', 'unix', 'os']
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
it 'provides api direct platform' do
|
|
@@ -75,7 +75,7 @@ describe 'v1 Connection Plugin' do
|
|
|
75
75
|
it 'provides family hierarchy' do
|
|
76
76
|
plat = Train::Platforms.name('linux')
|
|
77
77
|
family = connection.family_hierarchy(plat)
|
|
78
|
-
family.flatten.must_equal ['linux', 'unix']
|
|
78
|
+
family.flatten.must_equal ['linux', 'unix', 'os']
|
|
79
79
|
end
|
|
80
80
|
|
|
81
81
|
it 'must use the user-provided logger' do
|
|
@@ -14,6 +14,7 @@ describe 'ssh transport' do
|
|
|
14
14
|
host: rand.to_s,
|
|
15
15
|
password: rand.to_s,
|
|
16
16
|
key_files: rand.to_s,
|
|
17
|
+
proxy_command: 'ssh root@127.0.0.1 -W %h:%p',
|
|
17
18
|
}}
|
|
18
19
|
let(:cls_agent) { cls.new({ host: rand.to_s }) }
|
|
19
20
|
|
|
@@ -95,6 +96,7 @@ describe 'ssh transport' do
|
|
|
95
96
|
"-o", "IdentitiesOnly=yes",
|
|
96
97
|
"-o", "LogLevel=VERBOSE",
|
|
97
98
|
"-o", "ForwardAgent=no",
|
|
99
|
+
"-o", "ProxyCommand='ssh root@127.0.0.1 -W %h:%p'",
|
|
98
100
|
"-i", conf[:key_files],
|
|
99
101
|
"-p", "22",
|
|
100
102
|
"root@#{conf[:host]}",
|
|
@@ -120,6 +122,19 @@ describe 'ssh transport' do
|
|
|
120
122
|
cls_agent.stubs(:ssh_known_identities).returns({:some => 'rsa_key'})
|
|
121
123
|
cls_agent.connection
|
|
122
124
|
end
|
|
125
|
+
|
|
126
|
+
it 'sets up a proxy when ssh proxy command is specified' do
|
|
127
|
+
mock = MiniTest::Mock.new
|
|
128
|
+
mock.expect(:call, true) do |hostname, username, options|
|
|
129
|
+
options[:proxy].kind_of?(Net::SSH::Proxy::Command) &&
|
|
130
|
+
'ssh root@127.0.0.1 -W %h:%p' == options[:proxy].command_line_template
|
|
131
|
+
end
|
|
132
|
+
connection.stubs(:run_command)
|
|
133
|
+
Net::SSH.stub(:start, mock) do
|
|
134
|
+
connection.wait_until_ready
|
|
135
|
+
end
|
|
136
|
+
mock.verify
|
|
137
|
+
end
|
|
123
138
|
end
|
|
124
139
|
|
|
125
140
|
describe 'converting connection to string for logging' do
|
|
@@ -154,6 +169,7 @@ describe 'ssh transport' do
|
|
|
154
169
|
it 'wont connect if it is not possible' do
|
|
155
170
|
conf[:host] = 'localhost'
|
|
156
171
|
conf[:port] = 1
|
|
172
|
+
conf.delete :proxy_command
|
|
157
173
|
conn = cls.new(conf).connection
|
|
158
174
|
proc { conn.run_command('uname') }.must_raise Train::Transports::SSHFailed
|
|
159
175
|
end
|
data/test/windows/local_test.rb
CHANGED
|
@@ -12,16 +12,13 @@ require 'tempfile'
|
|
|
12
12
|
require 'train/transports/local'
|
|
13
13
|
|
|
14
14
|
describe 'windows local command' do
|
|
15
|
-
let(:
|
|
15
|
+
let(:backend) do
|
|
16
16
|
# get final config
|
|
17
17
|
target_config = Train.target_config({})
|
|
18
18
|
# initialize train
|
|
19
19
|
backend = Train.create('local', target_config)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
conn = backend.connection
|
|
23
|
-
conn
|
|
24
|
-
}
|
|
20
|
+
end
|
|
21
|
+
let(:conn) { backend.connection }
|
|
25
22
|
|
|
26
23
|
it 'verify os' do
|
|
27
24
|
os = conn.os
|
|
@@ -37,6 +34,65 @@ describe 'windows local command' do
|
|
|
37
34
|
cmd.stderr.must_equal ''
|
|
38
35
|
end
|
|
39
36
|
|
|
37
|
+
describe 'force 64 bit powershell command' do
|
|
38
|
+
let(:runner) { conn.instance_variable_get(:@runner) }
|
|
39
|
+
let(:powershell) { runner.instance_variable_get(:@powershell_cmd) }
|
|
40
|
+
RUBY_PLATFORM_DUP = RUBY_PLATFORM.dup
|
|
41
|
+
|
|
42
|
+
def override_platform(platform)
|
|
43
|
+
::Object.send(:remove_const, :RUBY_PLATFORM)
|
|
44
|
+
::Object.const_set(:RUBY_PLATFORM, platform)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
after do
|
|
48
|
+
backend.instance_variable_set(:@connection, nil)
|
|
49
|
+
::Object.send(:remove_const, :RUBY_PLATFORM)
|
|
50
|
+
::Object.const_set(:RUBY_PLATFORM, RUBY_PLATFORM_DUP)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it 'use normal powershell with PipeRunner' do
|
|
54
|
+
Train::Transports::Local::Connection::WindowsPipeRunner
|
|
55
|
+
.any_instance
|
|
56
|
+
.expects(:acquire_pipe)
|
|
57
|
+
.returns('acquired')
|
|
58
|
+
|
|
59
|
+
override_platform('x64-mingw32')
|
|
60
|
+
powershell.must_equal 'powershell'
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it 'use 64bit powershell with PipeRunner' do
|
|
64
|
+
Train::Transports::Local::Connection::WindowsPipeRunner
|
|
65
|
+
.any_instance
|
|
66
|
+
.expects(:acquire_pipe)
|
|
67
|
+
.returns('acquired')
|
|
68
|
+
|
|
69
|
+
override_platform('i386-mingw32')
|
|
70
|
+
powershell.must_equal "#{ENV['SystemRoot']}\\sysnative\\WindowsPowerShell\\v1.0\\powershell.exe"
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it 'use normal powershell with ShellRunner' do
|
|
74
|
+
Train::Transports::Local::Connection::WindowsPipeRunner
|
|
75
|
+
.any_instance
|
|
76
|
+
.expects(:acquire_pipe)
|
|
77
|
+
.returns(nil)
|
|
78
|
+
|
|
79
|
+
override_platform('x64-mingw32')
|
|
80
|
+
runner.class.must_equal Train::Transports::Local::Connection::WindowsShellRunner
|
|
81
|
+
powershell.must_equal 'powershell'
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
it 'use 64bit powershell with ShellRunner' do
|
|
85
|
+
Train::Transports::Local::Connection::WindowsPipeRunner
|
|
86
|
+
.any_instance
|
|
87
|
+
.expects(:acquire_pipe)
|
|
88
|
+
.returns(nil)
|
|
89
|
+
|
|
90
|
+
override_platform('i386-mingw32')
|
|
91
|
+
runner.class.must_equal Train::Transports::Local::Connection::WindowsShellRunner
|
|
92
|
+
powershell.must_equal "#{ENV['SystemRoot']}\\sysnative\\WindowsPowerShell\\v1.0\\powershell.exe"
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
40
96
|
it 'use powershell piping' do
|
|
41
97
|
cmd = conn.run_command("New-Object -Type PSObject | Add-Member -MemberType NoteProperty -Name A -Value (Write-Output 'PropertyA') -PassThru | Add-Member -MemberType NoteProperty -Name B -Value (Write-Output 'PropertyB') -PassThru | ConvertTo-Json")
|
|
42
98
|
cmd.stdout.must_equal "{\r\n \"A\": \"PropertyA\",\r\n \"B\": \"PropertyB\"\r\n}\r\n"
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: train
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dominik Richter
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2018-
|
|
11
|
+
date: 2018-03-15 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: json
|
|
@@ -316,7 +316,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
316
316
|
version: '0'
|
|
317
317
|
requirements: []
|
|
318
318
|
rubyforge_project:
|
|
319
|
-
rubygems_version: 2.
|
|
319
|
+
rubygems_version: 2.6.14
|
|
320
320
|
signing_key:
|
|
321
321
|
specification_version: 4
|
|
322
322
|
summary: Transport interface to talk to different backends.
|