train 3.2.14 → 3.2.20
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- metadata +29 -149
- data/LICENSE +0 -201
- data/lib/train.rb +0 -193
- data/lib/train/errors.rb +0 -44
- data/lib/train/extras.rb +0 -11
- data/lib/train/extras/command_wrapper.rb +0 -201
- data/lib/train/extras/stat.rb +0 -136
- data/lib/train/file.rb +0 -212
- data/lib/train/file/local.rb +0 -82
- data/lib/train/file/local/unix.rb +0 -96
- data/lib/train/file/local/windows.rb +0 -68
- data/lib/train/file/remote.rb +0 -40
- data/lib/train/file/remote/aix.rb +0 -29
- data/lib/train/file/remote/linux.rb +0 -21
- data/lib/train/file/remote/qnx.rb +0 -41
- data/lib/train/file/remote/unix.rb +0 -110
- data/lib/train/file/remote/windows.rb +0 -110
- data/lib/train/globals.rb +0 -5
- data/lib/train/options.rb +0 -81
- data/lib/train/platforms.rb +0 -102
- data/lib/train/platforms/common.rb +0 -34
- data/lib/train/platforms/detect.rb +0 -12
- data/lib/train/platforms/detect/helpers/os_common.rb +0 -160
- data/lib/train/platforms/detect/helpers/os_linux.rb +0 -80
- data/lib/train/platforms/detect/helpers/os_windows.rb +0 -142
- data/lib/train/platforms/detect/scanner.rb +0 -85
- data/lib/train/platforms/detect/specifications/api.rb +0 -20
- data/lib/train/platforms/detect/specifications/os.rb +0 -629
- data/lib/train/platforms/detect/uuid.rb +0 -32
- data/lib/train/platforms/family.rb +0 -31
- data/lib/train/platforms/platform.rb +0 -109
- data/lib/train/plugin_test_helper.rb +0 -51
- data/lib/train/plugins.rb +0 -40
- data/lib/train/plugins/base_connection.rb +0 -198
- data/lib/train/plugins/transport.rb +0 -49
- data/lib/train/transports/cisco_ios_connection.rb +0 -133
- data/lib/train/transports/local.rb +0 -240
- data/lib/train/transports/mock.rb +0 -183
- data/lib/train/transports/ssh.rb +0 -271
- data/lib/train/transports/ssh_connection.rb +0 -342
- data/lib/train/version.rb +0 -7
data/lib/train/extras/stat.rb
DELETED
@@ -1,136 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
# author: Dominik Richter
|
3
|
-
# author: Christoph Hartmann
|
4
|
-
module Train::Extras
|
5
|
-
class Stat
|
6
|
-
TYPES = {
|
7
|
-
socket: 00140000,
|
8
|
-
symlink: 00120000,
|
9
|
-
file: 00100000,
|
10
|
-
block_device: 00060000,
|
11
|
-
directory: 00040000,
|
12
|
-
character_device: 00020000,
|
13
|
-
pipe: 00010000,
|
14
|
-
}.freeze
|
15
|
-
|
16
|
-
def self.find_type(mode)
|
17
|
-
res = TYPES.find { |_, mask| mask & mode == mask }
|
18
|
-
res.nil? ? :unknown : res[0]
|
19
|
-
end
|
20
|
-
|
21
|
-
def self.stat(shell_escaped_path, backend, follow_symlink)
|
22
|
-
# use perl scripts for aix, solaris 10 and hpux
|
23
|
-
if backend.os.aix? || (backend.os.solaris? && backend.os[:release].to_i < 11) || backend.os.hpux?
|
24
|
-
return aix_stat(shell_escaped_path, backend, follow_symlink)
|
25
|
-
end
|
26
|
-
return bsd_stat(shell_escaped_path, backend, follow_symlink) if backend.os.bsd?
|
27
|
-
# linux,solaris 11 and esx will use standard linux stats
|
28
|
-
return linux_stat(shell_escaped_path, backend, follow_symlink) if backend.os.unix? || backend.os.esx?
|
29
|
-
|
30
|
-
# all other cases we don't handle
|
31
|
-
# TODO: print an error if we get here, as it shouldn't be invoked
|
32
|
-
# on non-unix
|
33
|
-
{}
|
34
|
-
end
|
35
|
-
|
36
|
-
def self.linux_stat(shell_escaped_path, backend, follow_symlink)
|
37
|
-
lstat = follow_symlink ? " -L" : ""
|
38
|
-
format = (backend.os.esx? || backend.os[:name] == "alpine" || backend.os[:name] == "yocto") ? "-c" : "--printf"
|
39
|
-
res = backend.run_command("stat#{lstat} #{shell_escaped_path} 2>/dev/null #{format} '%s\n%f\n%U\n%u\n%G\n%g\n%X\n%Y\n%C'")
|
40
|
-
# ignore the exit_code: it is != 0 if selinux labels are not supported
|
41
|
-
# on the system.
|
42
|
-
|
43
|
-
fields = res.stdout.split("\n")
|
44
|
-
return {} if fields.length != 9
|
45
|
-
|
46
|
-
tmask = fields[1].to_i(16)
|
47
|
-
selinux = fields[8]
|
48
|
-
## selinux security context string not available on esxi
|
49
|
-
selinux = nil if (selinux == "?") || (selinux == "(null)") || (selinux == "C")
|
50
|
-
{
|
51
|
-
type: find_type(tmask),
|
52
|
-
mode: tmask & 07777,
|
53
|
-
owner: fields[2],
|
54
|
-
uid: fields[3].to_i,
|
55
|
-
group: fields[4],
|
56
|
-
gid: fields[5].to_i,
|
57
|
-
mtime: fields[7].to_i,
|
58
|
-
size: fields[0].to_i,
|
59
|
-
selinux_label: selinux,
|
60
|
-
}
|
61
|
-
end
|
62
|
-
|
63
|
-
def self.bsd_stat(shell_escaped_path, backend, follow_symlink)
|
64
|
-
# From stat man page on FreeBSD:
|
65
|
-
# z The size of file in bytes (st_size).
|
66
|
-
# p File type and permissions (st_mode).
|
67
|
-
# u, g User ID and group ID of file's owner (st_uid, st_gid).
|
68
|
-
# a, m, c, B
|
69
|
-
# The time file was last accessed or modified, or when the
|
70
|
-
# inode was last changed, or the birth time of the inode
|
71
|
-
# (st_atime, st_mtime, st_ctime, st_birthtime).
|
72
|
-
#
|
73
|
-
# The special output specifier S may be used to indicate that the
|
74
|
-
# output, if applicable, should be in string format. May be used
|
75
|
-
# in combination with:
|
76
|
-
# ...
|
77
|
-
# gu Display group or user name.
|
78
|
-
lstat = follow_symlink ? " -L" : ""
|
79
|
-
res = backend.run_command(
|
80
|
-
"stat#{lstat} -f '%z\n%p\n%Su\n%u\n%Sg\n%g\n%a\n%m' "\
|
81
|
-
"#{shell_escaped_path}"
|
82
|
-
)
|
83
|
-
|
84
|
-
return {} if res.exit_status != 0
|
85
|
-
|
86
|
-
fields = res.stdout.split("\n")
|
87
|
-
return {} if fields.length != 8
|
88
|
-
|
89
|
-
tmask = fields[1].to_i(8)
|
90
|
-
|
91
|
-
{
|
92
|
-
type: find_type(tmask),
|
93
|
-
mode: tmask & 07777,
|
94
|
-
owner: fields[2],
|
95
|
-
uid: fields[3].to_i,
|
96
|
-
group: fields[4],
|
97
|
-
gid: fields[5].to_i,
|
98
|
-
mtime: fields[7].to_i,
|
99
|
-
size: fields[0].to_i,
|
100
|
-
selinux_label: fields[8],
|
101
|
-
}
|
102
|
-
end
|
103
|
-
|
104
|
-
def self.aix_stat(shell_escaped_path, backend, follow_symlink)
|
105
|
-
# Perl here b/c it is default on AIX
|
106
|
-
lstat = follow_symlink ? "lstat" : "stat"
|
107
|
-
stat_cmd = <<-EOP
|
108
|
-
perl -e '
|
109
|
-
@a = #{lstat}(shift) or exit 2;
|
110
|
-
$u = getpwuid($a[4]);
|
111
|
-
$g = getgrgid($a[5]);
|
112
|
-
printf("0%o\\n%s\\n%d\\n%s\\n%d\\n%d\\n%d\\n", $a[2], $u, $a[4], $g, $a[5], $a[9], $a[7])
|
113
|
-
' #{shell_escaped_path}
|
114
|
-
EOP
|
115
|
-
|
116
|
-
res = backend.run_command(stat_cmd)
|
117
|
-
return {} if res.exit_status != 0
|
118
|
-
|
119
|
-
fields = res.stdout.split("\n")
|
120
|
-
return {} if fields.length != 7
|
121
|
-
|
122
|
-
tmask = fields[0].to_i(8)
|
123
|
-
{
|
124
|
-
type: find_type(tmask),
|
125
|
-
mode: tmask & 07777,
|
126
|
-
owner: fields[1],
|
127
|
-
uid: fields[2].to_i,
|
128
|
-
group: fields[3],
|
129
|
-
gid: fields[4].to_i,
|
130
|
-
mtime: fields[5].to_i,
|
131
|
-
size: fields[6].to_i,
|
132
|
-
selinux_label: nil,
|
133
|
-
}
|
134
|
-
end
|
135
|
-
end
|
136
|
-
end
|
data/lib/train/file.rb
DELETED
@@ -1,212 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
#
|
3
|
-
# author: Christoph Hartmann
|
4
|
-
# author: Dominik Richter
|
5
|
-
|
6
|
-
require_relative "file/local"
|
7
|
-
require_relative "file/remote"
|
8
|
-
require_relative "extras/stat"
|
9
|
-
|
10
|
-
module Train
|
11
|
-
class File # rubocop:disable Metrics/ClassLength
|
12
|
-
def initialize(backend, path, follow_symlink = true)
|
13
|
-
@backend = backend
|
14
|
-
@path = path || ""
|
15
|
-
@follow_symlink = follow_symlink
|
16
|
-
|
17
|
-
sanitize_filename(path)
|
18
|
-
end
|
19
|
-
|
20
|
-
# This method gets override by particular os class.
|
21
|
-
def sanitize_filename(_path)
|
22
|
-
nil
|
23
|
-
end
|
24
|
-
|
25
|
-
# interface methods: these fields should be implemented by every
|
26
|
-
# backend File
|
27
|
-
DATA_FIELDS = %w{
|
28
|
-
exist? mode owner group uid gid content mtime size selinux_label path
|
29
|
-
}.freeze
|
30
|
-
|
31
|
-
DATA_FIELDS.each do |m|
|
32
|
-
next if m == "path"
|
33
|
-
|
34
|
-
define_method m do
|
35
|
-
raise NotImplementedError, "File must implement the #{m}() method."
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def to_json
|
40
|
-
res = Hash[DATA_FIELDS.map { |x| [x, method(x).call] }]
|
41
|
-
# additional fields provided as input
|
42
|
-
res["type"] = type
|
43
|
-
res["follow_symlink"] = @follow_symlink
|
44
|
-
res
|
45
|
-
end
|
46
|
-
|
47
|
-
def type
|
48
|
-
:unknown
|
49
|
-
end
|
50
|
-
|
51
|
-
def source
|
52
|
-
if @follow_symlink
|
53
|
-
self.class.new(@backend, @path, false)
|
54
|
-
else
|
55
|
-
self
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def source_path
|
60
|
-
@path
|
61
|
-
end
|
62
|
-
|
63
|
-
# product_version is primarily used by Windows operating systems only and will be overwritten
|
64
|
-
# in Windows-related classes. Since this field is returned for all file objects, the acceptable
|
65
|
-
# default value is nil
|
66
|
-
def product_version
|
67
|
-
nil
|
68
|
-
end
|
69
|
-
|
70
|
-
# file_version is primarily used by Windows operating systems only and will be overwritten
|
71
|
-
# in Windows-related classes. Since this field is returned for all file objects, the acceptable
|
72
|
-
# default value is nil
|
73
|
-
def file_version
|
74
|
-
nil
|
75
|
-
end
|
76
|
-
|
77
|
-
def version?(version)
|
78
|
-
(product_version == version) ||
|
79
|
-
(file_version == version)
|
80
|
-
end
|
81
|
-
|
82
|
-
def block_device?
|
83
|
-
type.to_s == "block_device"
|
84
|
-
end
|
85
|
-
|
86
|
-
def character_device?
|
87
|
-
type.to_s == "character_device"
|
88
|
-
end
|
89
|
-
|
90
|
-
def pipe?
|
91
|
-
type.to_s == "pipe"
|
92
|
-
end
|
93
|
-
|
94
|
-
def file?
|
95
|
-
type.to_s == "file"
|
96
|
-
end
|
97
|
-
|
98
|
-
def socket?
|
99
|
-
type.to_s == "socket"
|
100
|
-
end
|
101
|
-
|
102
|
-
def directory?
|
103
|
-
type.to_s == "directory"
|
104
|
-
end
|
105
|
-
|
106
|
-
def symlink?
|
107
|
-
source.type.to_s == "symlink"
|
108
|
-
end
|
109
|
-
|
110
|
-
def owned_by?(sth)
|
111
|
-
owner == sth
|
112
|
-
end
|
113
|
-
|
114
|
-
def path
|
115
|
-
if symlink? && @follow_symlink
|
116
|
-
link_path
|
117
|
-
else
|
118
|
-
@path
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
# if the OS-specific file class supports inquirying as to whether the
|
123
|
-
# file/device is mounted, the #mounted method should return a command
|
124
|
-
# object whose stdout will not be nil if indeed the device is mounted.
|
125
|
-
#
|
126
|
-
# if the OS-specific file class does not support checking for mount
|
127
|
-
# status, the method should not be implemented and this method will
|
128
|
-
# return false.
|
129
|
-
def mounted?
|
130
|
-
return false unless respond_to?(:mounted)
|
131
|
-
|
132
|
-
!mounted.nil? && !mounted.stdout.nil? && !mounted.stdout.empty?
|
133
|
-
end
|
134
|
-
|
135
|
-
def md5sum
|
136
|
-
# Skip processing rest of method if fallback method is selected
|
137
|
-
return perform_checksum_ruby(:md5) if defined?(@ruby_checksum_fallback)
|
138
|
-
|
139
|
-
checksum = if @backend.os.family == "windows"
|
140
|
-
perform_checksum_windows(:md5)
|
141
|
-
else
|
142
|
-
@md5_command ||= case @backend.os.family
|
143
|
-
when "darwin"
|
144
|
-
"md5 -r"
|
145
|
-
when "solaris"
|
146
|
-
"digest -a md5"
|
147
|
-
else
|
148
|
-
"md5sum"
|
149
|
-
end
|
150
|
-
|
151
|
-
perform_checksum_unix(@md5_command)
|
152
|
-
end
|
153
|
-
|
154
|
-
checksum || perform_checksum_ruby(:md5)
|
155
|
-
end
|
156
|
-
|
157
|
-
def sha256sum
|
158
|
-
# Skip processing rest of method if fallback method is selected
|
159
|
-
return perform_checksum_ruby(:sha256) if defined?(@ruby_checksum_fallback)
|
160
|
-
|
161
|
-
checksum = if @backend.os.family == "windows"
|
162
|
-
perform_checksum_windows(:sha256)
|
163
|
-
else
|
164
|
-
@sha256_command ||= case @backend.os.family
|
165
|
-
when "darwin", "hpux", "qnx"
|
166
|
-
"shasum -a 256"
|
167
|
-
when "solaris"
|
168
|
-
"digest -a sha256"
|
169
|
-
else
|
170
|
-
"sha256sum"
|
171
|
-
end
|
172
|
-
|
173
|
-
perform_checksum_unix(@sha256_command)
|
174
|
-
end
|
175
|
-
|
176
|
-
checksum || perform_checksum_ruby(:sha256)
|
177
|
-
end
|
178
|
-
|
179
|
-
private
|
180
|
-
|
181
|
-
def perform_checksum_unix(cmd)
|
182
|
-
res = @backend.run_command("#{cmd} #{@path}")
|
183
|
-
res.stdout.split(" ").first if res.exit_status == 0
|
184
|
-
end
|
185
|
-
|
186
|
-
def perform_checksum_windows(method)
|
187
|
-
cmd = "CertUtil -hashfile #{@path} #{method.to_s.upcase}"
|
188
|
-
res = @backend.run_command(cmd)
|
189
|
-
res.stdout.split("\r\n")[1].tr(" ", "") if res.exit_status == 0
|
190
|
-
end
|
191
|
-
|
192
|
-
# This pulls the content of the file to the machine running Train and uses
|
193
|
-
# Digest to perform the checksum. This is less efficient than using remote
|
194
|
-
# system binaries and can lead to incorrect results due to encoding.
|
195
|
-
def perform_checksum_ruby(method)
|
196
|
-
# This is used to skip attempting other checksum methods. If this is set
|
197
|
-
# then we know all other methods have failed.
|
198
|
-
@ruby_checksum_fallback = true
|
199
|
-
case method
|
200
|
-
when :md5
|
201
|
-
res = Digest::MD5.new
|
202
|
-
when :sha256
|
203
|
-
res = Digest::SHA256.new
|
204
|
-
end
|
205
|
-
|
206
|
-
res.update(content)
|
207
|
-
res.hexdigest
|
208
|
-
rescue TypeError => _
|
209
|
-
nil
|
210
|
-
end
|
211
|
-
end
|
212
|
-
end
|
data/lib/train/file/local.rb
DELETED
@@ -1,82 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
module Train
|
4
|
-
class File
|
5
|
-
class Local < Train::File
|
6
|
-
%w{
|
7
|
-
exist? file? socket? directory? symlink? pipe? size basename
|
8
|
-
}.each do |m|
|
9
|
-
define_method m.to_sym do
|
10
|
-
::File.method(m.to_sym).call(@path)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
def content
|
15
|
-
@content ||= ::File.read(@path, encoding: "UTF-8")
|
16
|
-
rescue StandardError => _
|
17
|
-
nil
|
18
|
-
end
|
19
|
-
|
20
|
-
def link_path
|
21
|
-
return nil unless symlink?
|
22
|
-
|
23
|
-
begin
|
24
|
-
@link_path ||= ::File.realpath(@path)
|
25
|
-
rescue Errno::ELOOP => _
|
26
|
-
# Leave it blank on symbolic loop, same as readlink
|
27
|
-
@link_path = ""
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def shallow_link_path
|
32
|
-
return nil unless symlink?
|
33
|
-
|
34
|
-
@link_path ||= ::File.readlink(@path)
|
35
|
-
end
|
36
|
-
|
37
|
-
def block_device?
|
38
|
-
::File.blockdev?(@path)
|
39
|
-
end
|
40
|
-
|
41
|
-
def character_device?
|
42
|
-
::File.chardev?(@path)
|
43
|
-
end
|
44
|
-
|
45
|
-
def type
|
46
|
-
case ::File.ftype(@path)
|
47
|
-
when "blockSpecial"
|
48
|
-
:block_device
|
49
|
-
when "characterSpecial"
|
50
|
-
:character_device
|
51
|
-
when "link"
|
52
|
-
:symlink
|
53
|
-
when "fifo"
|
54
|
-
:pipe
|
55
|
-
else
|
56
|
-
::File.ftype(@path).to_sym
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
%w{
|
61
|
-
mode owner group uid gid mtime selinux_label
|
62
|
-
}.each do |field|
|
63
|
-
define_method field.to_sym do
|
64
|
-
stat[field.to_sym]
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
def mode?(sth)
|
69
|
-
mode == sth
|
70
|
-
end
|
71
|
-
|
72
|
-
def linked_to?(dst)
|
73
|
-
link_path == dst
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
# subclass requires are loaded after Train::File::Local is defined
|
80
|
-
# to avoid superclass mismatch errors
|
81
|
-
require_relative "local/unix"
|
82
|
-
require_relative "local/windows"
|
@@ -1,96 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require "shellwords"
|
4
|
-
require_relative "../../extras/stat"
|
5
|
-
|
6
|
-
module Train
|
7
|
-
class File
|
8
|
-
class Local
|
9
|
-
class Unix < Train::File::Local
|
10
|
-
def sanitize_filename(path)
|
11
|
-
@spath = Shellwords.escape(path) || @path
|
12
|
-
end
|
13
|
-
|
14
|
-
def stat
|
15
|
-
return @stat if defined?(@stat)
|
16
|
-
|
17
|
-
begin
|
18
|
-
file_stat =
|
19
|
-
if @follow_symlink
|
20
|
-
::File.stat(@path)
|
21
|
-
else
|
22
|
-
::File.lstat(@path)
|
23
|
-
end
|
24
|
-
rescue StandardError => _err
|
25
|
-
return @stat = {}
|
26
|
-
end
|
27
|
-
|
28
|
-
@stat = {
|
29
|
-
type: Train::Extras::Stat.find_type(file_stat.mode),
|
30
|
-
mode: file_stat.mode & 07777,
|
31
|
-
mtime: file_stat.mtime.to_i,
|
32
|
-
size: file_stat.size,
|
33
|
-
owner: pw_username(file_stat.uid),
|
34
|
-
uid: file_stat.uid,
|
35
|
-
group: pw_groupname(file_stat.gid),
|
36
|
-
gid: file_stat.gid,
|
37
|
-
}
|
38
|
-
|
39
|
-
lstat = @follow_symlink ? " -L" : ""
|
40
|
-
res = @backend.run_command("stat#{lstat} #{@spath} 2>/dev/null --printf '%C'")
|
41
|
-
if res.exit_status == 0 && !res.stdout.empty? && res.stdout != "?"
|
42
|
-
@stat[:selinux_label] = res.stdout.strip
|
43
|
-
end
|
44
|
-
|
45
|
-
@stat
|
46
|
-
end
|
47
|
-
|
48
|
-
def mounted
|
49
|
-
@mounted ||=
|
50
|
-
@backend.run_command("mount | grep -- ' on #{@path} '")
|
51
|
-
end
|
52
|
-
|
53
|
-
def grouped_into?(sth)
|
54
|
-
group == sth
|
55
|
-
end
|
56
|
-
|
57
|
-
def unix_mode_mask(owner, type)
|
58
|
-
o = UNIX_MODE_OWNERS[owner.to_sym]
|
59
|
-
return nil if o.nil?
|
60
|
-
|
61
|
-
t = UNIX_MODE_TYPES[type.to_sym]
|
62
|
-
return nil if t.nil?
|
63
|
-
|
64
|
-
t & o
|
65
|
-
end
|
66
|
-
|
67
|
-
private
|
68
|
-
|
69
|
-
def pw_username(uid)
|
70
|
-
Etc.getpwuid(uid).name
|
71
|
-
rescue ArgumentError => _
|
72
|
-
nil
|
73
|
-
end
|
74
|
-
|
75
|
-
def pw_groupname(gid)
|
76
|
-
Etc.getgrgid(gid).name
|
77
|
-
rescue ArgumentError => _
|
78
|
-
nil
|
79
|
-
end
|
80
|
-
|
81
|
-
UNIX_MODE_OWNERS = {
|
82
|
-
all: 00777,
|
83
|
-
owner: 00700,
|
84
|
-
group: 00070,
|
85
|
-
other: 00007,
|
86
|
-
}.freeze
|
87
|
-
|
88
|
-
UNIX_MODE_TYPES = {
|
89
|
-
r: 00444,
|
90
|
-
w: 00222,
|
91
|
-
x: 00111,
|
92
|
-
}.freeze
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|