train-core 1.4.4
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 +7 -0
- data/CHANGELOG.md +780 -0
- data/Gemfile +36 -0
- data/LICENSE +201 -0
- data/README.md +197 -0
- data/lib/train.rb +158 -0
- data/lib/train/errors.rb +32 -0
- data/lib/train/extras.rb +11 -0
- data/lib/train/extras/command_wrapper.rb +137 -0
- data/lib/train/extras/stat.rb +132 -0
- data/lib/train/file.rb +151 -0
- data/lib/train/file/local.rb +75 -0
- data/lib/train/file/local/unix.rb +96 -0
- data/lib/train/file/local/windows.rb +63 -0
- data/lib/train/file/remote.rb +36 -0
- data/lib/train/file/remote/aix.rb +21 -0
- data/lib/train/file/remote/linux.rb +19 -0
- data/lib/train/file/remote/qnx.rb +41 -0
- data/lib/train/file/remote/unix.rb +106 -0
- data/lib/train/file/remote/windows.rb +94 -0
- data/lib/train/options.rb +80 -0
- data/lib/train/platforms.rb +84 -0
- data/lib/train/platforms/common.rb +34 -0
- data/lib/train/platforms/detect.rb +12 -0
- data/lib/train/platforms/detect/helpers/os_common.rb +145 -0
- data/lib/train/platforms/detect/helpers/os_linux.rb +75 -0
- data/lib/train/platforms/detect/helpers/os_windows.rb +120 -0
- data/lib/train/platforms/detect/scanner.rb +84 -0
- data/lib/train/platforms/detect/specifications/api.rb +15 -0
- data/lib/train/platforms/detect/specifications/os.rb +578 -0
- data/lib/train/platforms/detect/uuid.rb +34 -0
- data/lib/train/platforms/family.rb +26 -0
- data/lib/train/platforms/platform.rb +101 -0
- data/lib/train/plugins.rb +40 -0
- data/lib/train/plugins/base_connection.rb +169 -0
- data/lib/train/plugins/transport.rb +49 -0
- data/lib/train/transports/local.rb +232 -0
- data/lib/train/version.rb +7 -0
- data/train-core.gemspec +27 -0
- metadata +116 -0
@@ -0,0 +1,75 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Train::Platforms::Detect::Helpers
|
4
|
+
module Linux
|
5
|
+
def redhatish_platform(conf)
|
6
|
+
conf =~ /^red hat/i ? 'redhat' : /(\w+)/i.match(conf)[1].downcase
|
7
|
+
end
|
8
|
+
|
9
|
+
def redhatish_version(conf)
|
10
|
+
case conf
|
11
|
+
when /rawhide/i
|
12
|
+
/((\d+) \(Rawhide\))/i.match(conf)[1].downcase
|
13
|
+
when /derived from .*linux/i
|
14
|
+
/Linux ((\d+|\.)+)/i.match(conf)[1]
|
15
|
+
else
|
16
|
+
/release ([\d\.]+)/.match(conf)[1]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def linux_os_release
|
21
|
+
data = unix_file_contents('/etc/os-release')
|
22
|
+
return if data.nil?
|
23
|
+
|
24
|
+
os_info = parse_os_release_info(data)
|
25
|
+
cisco_info_file = os_info['CISCO_RELEASE_INFO']
|
26
|
+
if cisco_info_file
|
27
|
+
os_info.merge!(parse_os_release_info(unix_file_contents(cisco_info_file)))
|
28
|
+
end
|
29
|
+
|
30
|
+
os_info
|
31
|
+
end
|
32
|
+
|
33
|
+
def parse_os_release_info(raw)
|
34
|
+
return {} if raw.nil?
|
35
|
+
|
36
|
+
raw.lines.each_with_object({}) do |line, memo|
|
37
|
+
line.strip!
|
38
|
+
next if line.empty?
|
39
|
+
key, value = line.split('=', 2)
|
40
|
+
memo[key] = value.gsub(/\A"|"\Z/, '') unless value.empty?
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def lsb_config(content)
|
45
|
+
id = /^DISTRIB_ID=["']?(.+?)["']?$/.match(content)
|
46
|
+
release = /^DISTRIB_RELEASE=["']?(.+?)["']?$/.match(content)
|
47
|
+
codename = /^DISTRIB_CODENAME=["']?(.+?)["']?$/.match(content)
|
48
|
+
{
|
49
|
+
id: id.nil? ? nil : id[1],
|
50
|
+
release: release.nil? ? nil : release[1],
|
51
|
+
codename: codename.nil? ? nil : codename[1],
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
def lsb_release(content)
|
56
|
+
id = /^Distributor ID:\s+(.+)$/.match(content)
|
57
|
+
release = /^Release:\s+(.+)$/.match(content)
|
58
|
+
codename = /^Codename:\s+(.+)$/.match(content)
|
59
|
+
{
|
60
|
+
id: id.nil? ? nil : id[1],
|
61
|
+
release: release.nil? ? nil : release[1],
|
62
|
+
codename: codename.nil? ? nil : codename[1],
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
def read_linux_lsb
|
67
|
+
return @lsb unless @lsb.empty?
|
68
|
+
if !(raw = unix_file_contents('/etc/lsb-release')).nil?
|
69
|
+
@lsb = lsb_config(raw)
|
70
|
+
elsif !(raw = unix_file_contents('/usr/bin/lsb-release')).nil?
|
71
|
+
@lsb = lsb_release(raw)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Train::Platforms::Detect::Helpers
|
4
|
+
module Windows
|
5
|
+
def detect_windows
|
6
|
+
res = @backend.run_command('cmd /c ver')
|
7
|
+
return false if res.exit_status != 0 or res.stdout.empty?
|
8
|
+
|
9
|
+
# if the ver contains `Windows`, we know its a Windows system
|
10
|
+
version = res.stdout.strip
|
11
|
+
return false unless version.downcase =~ /windows/
|
12
|
+
@platform[:family] = 'windows'
|
13
|
+
|
14
|
+
# try to extract release from eg. `Microsoft Windows [Version 6.3.9600]`
|
15
|
+
release = /\[(?<name>.*)\]/.match(version)
|
16
|
+
unless release[:name].nil?
|
17
|
+
# release is 6.3.9600 now
|
18
|
+
@platform[:release] = release[:name].downcase.gsub('version', '').strip
|
19
|
+
# fallback, if we are not able to extract the name from wmic later
|
20
|
+
@platform[:name] = "Windows #{@platform[:release]}"
|
21
|
+
end
|
22
|
+
|
23
|
+
# try to use wmic, but lets keep it optional
|
24
|
+
read_wmic
|
25
|
+
|
26
|
+
true
|
27
|
+
end
|
28
|
+
|
29
|
+
# reads os name and version from wmic
|
30
|
+
# @see https://msdn.microsoft.com/en-us/library/bb742610.aspx#EEAA
|
31
|
+
# Thanks to Matt Wrock (https://github.com/mwrock) for this hint
|
32
|
+
def read_wmic
|
33
|
+
res = @backend.run_command('wmic os get * /format:list')
|
34
|
+
if res.exit_status == 0
|
35
|
+
sys_info = {}
|
36
|
+
res.stdout.lines.each { |line|
|
37
|
+
m = /^\s*([^=]*?)\s*=\s*(.*?)\s*$/.match(line)
|
38
|
+
sys_info[m[1].to_sym] = m[2] unless m.nil? || m[1].nil?
|
39
|
+
}
|
40
|
+
|
41
|
+
@platform[:release] = sys_info[:Version]
|
42
|
+
# additional info on windows
|
43
|
+
@platform[:build] = sys_info[:BuildNumber]
|
44
|
+
@platform[:name] = sys_info[:Caption]
|
45
|
+
@platform[:name] = @platform[:name].gsub('Microsoft', '').strip unless @platform[:name].empty?
|
46
|
+
@platform[:arch] = read_wmic_cpu
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# `OSArchitecture` from `read_wmic` does not match a normal standard
|
51
|
+
# For example, `x86_64` shows as `64-bit`
|
52
|
+
def read_wmic_cpu
|
53
|
+
res = @backend.run_command('wmic cpu get architecture /format:list')
|
54
|
+
if res.exit_status == 0
|
55
|
+
sys_info = {}
|
56
|
+
res.stdout.lines.each { |line|
|
57
|
+
m = /^\s*([^=]*?)\s*=\s*(.*?)\s*$/.match(line)
|
58
|
+
sys_info[m[1].to_sym] = m[2] unless m.nil? || m[1].nil?
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
# This converts `wmic os get architecture` output to a normal standard
|
63
|
+
# https://msdn.microsoft.com/en-us/library/aa394373(VS.85).aspx
|
64
|
+
arch_map = {
|
65
|
+
0 => 'i386',
|
66
|
+
1 => 'mips',
|
67
|
+
2 => 'alpha',
|
68
|
+
3 => 'powerpc',
|
69
|
+
5 => 'arm',
|
70
|
+
6 => 'ia64',
|
71
|
+
9 => 'x86_64',
|
72
|
+
}
|
73
|
+
|
74
|
+
# The value of `wmic cpu get architecture` is always a number between 0-9
|
75
|
+
arch_number = sys_info[:Architecture].to_i
|
76
|
+
arch_map[arch_number]
|
77
|
+
end
|
78
|
+
|
79
|
+
# This method scans the target os for a unique uuid to use
|
80
|
+
def windows_uuid
|
81
|
+
uuid = windows_uuid_from_chef
|
82
|
+
uuid = windows_uuid_from_machine_file if uuid.nil?
|
83
|
+
uuid = windows_uuid_from_wmic if uuid.nil?
|
84
|
+
uuid = windows_uuid_from_registry if uuid.nil?
|
85
|
+
raise Train::TransportError, 'Cannot find a UUID for your node.' if uuid.nil?
|
86
|
+
uuid
|
87
|
+
end
|
88
|
+
|
89
|
+
def windows_uuid_from_machine_file
|
90
|
+
%W(
|
91
|
+
#{ENV['SYSTEMDRIVE']}\\chef\\chef_guid
|
92
|
+
#{ENV['HOMEDRIVE']}#{ENV['HOMEPATH']}\\.chef\\chef_guid
|
93
|
+
).each do |path|
|
94
|
+
file = @backend.file(path)
|
95
|
+
return file.content.chomp if file.exist? && !file.size.zero?
|
96
|
+
end
|
97
|
+
nil
|
98
|
+
end
|
99
|
+
|
100
|
+
def windows_uuid_from_chef
|
101
|
+
file = @backend.file("#{ENV['SYSTEMDRIVE']}\\chef\\cache\\data_collector_metadata.json")
|
102
|
+
return if !file.exist? || file.size.zero?
|
103
|
+
json = JSON.parse(file.content)
|
104
|
+
json['node_uuid'] if json['node_uuid']
|
105
|
+
end
|
106
|
+
|
107
|
+
def windows_uuid_from_wmic
|
108
|
+
result = @backend.run_command('wmic csproduct get UUID')
|
109
|
+
return unless result.exit_status.zero?
|
110
|
+
result.stdout.split("\r\n")[-1].strip
|
111
|
+
end
|
112
|
+
|
113
|
+
def windows_uuid_from_registry
|
114
|
+
cmd = '(Get-ItemProperty "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography" -Name "MachineGuid")."MachineGuid"'
|
115
|
+
result = @backend.run_command(cmd)
|
116
|
+
return unless result.exit_status.zero?
|
117
|
+
result.stdout.chomp
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'train/platforms/detect/helpers/os_common'
|
4
|
+
|
5
|
+
module Train::Platforms::Detect
|
6
|
+
class Scanner
|
7
|
+
include Train::Platforms::Detect::Helpers::OSCommon
|
8
|
+
|
9
|
+
def initialize(backend)
|
10
|
+
@backend = backend
|
11
|
+
@platform = {}
|
12
|
+
@family_hierarchy = []
|
13
|
+
|
14
|
+
# detect cache variables
|
15
|
+
@files = {}
|
16
|
+
@uname = {}
|
17
|
+
@lsb = {}
|
18
|
+
@cache = {}
|
19
|
+
end
|
20
|
+
|
21
|
+
# Main detect method to scan all platforms for a match
|
22
|
+
#
|
23
|
+
# @return Train::Platform instance or error if none found
|
24
|
+
def scan
|
25
|
+
# start with the platform/families who have no families (the top levels)
|
26
|
+
top = Train::Platforms.top_platforms
|
27
|
+
top.each do |_name, plat|
|
28
|
+
# we are doing a instance_eval here to make sure we have the proper
|
29
|
+
# context with all the detect helper methods
|
30
|
+
next unless instance_eval(&plat.detect) == true
|
31
|
+
|
32
|
+
# if we have a match start looking at the children
|
33
|
+
plat_result = scan_children(plat)
|
34
|
+
next if plat_result.nil?
|
35
|
+
|
36
|
+
# return platform to backend
|
37
|
+
@family_hierarchy << plat.name
|
38
|
+
return get_platform(plat_result)
|
39
|
+
end
|
40
|
+
|
41
|
+
fail Train::PlatformDetectionFailed, 'Sorry, we are unable to detect your platform'
|
42
|
+
end
|
43
|
+
|
44
|
+
def scan_children(parent)
|
45
|
+
parent.children.each do |plat, condition|
|
46
|
+
next unless instance_eval(&plat.detect) == true
|
47
|
+
|
48
|
+
if plat.class == Train::Platforms::Platform
|
49
|
+
return plat if condition.empty? || check_condition(condition)
|
50
|
+
elsif plat.class == Train::Platforms::Family
|
51
|
+
plat = scan_family_children(plat)
|
52
|
+
return plat unless plat.nil?
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
nil
|
57
|
+
end
|
58
|
+
|
59
|
+
def scan_family_children(plat)
|
60
|
+
child_result = scan_children(plat) unless plat.children.nil?
|
61
|
+
return if child_result.nil?
|
62
|
+
@family_hierarchy << plat.name
|
63
|
+
child_result
|
64
|
+
end
|
65
|
+
|
66
|
+
def check_condition(condition)
|
67
|
+
condition.each do |k, v|
|
68
|
+
op, expected = v.strip.split(' ')
|
69
|
+
op = '==' if op == '='
|
70
|
+
return false if @platform[k].nil? || !instance_eval("'#{@platform[k]}' #{op} '#{expected}'")
|
71
|
+
end
|
72
|
+
|
73
|
+
true
|
74
|
+
end
|
75
|
+
|
76
|
+
def get_platform(plat)
|
77
|
+
plat.backend = @backend
|
78
|
+
plat.platform = @platform
|
79
|
+
plat.add_platform_methods
|
80
|
+
plat.family_hierarchy = @family_hierarchy
|
81
|
+
plat
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Train::Platforms::Detect::Specifications
|
4
|
+
class Api
|
5
|
+
def self.load
|
6
|
+
plat = Train::Platforms
|
7
|
+
|
8
|
+
plat.family('api')
|
9
|
+
|
10
|
+
plat.family('cloud').in_family('api')
|
11
|
+
plat.name('aws').in_family('cloud')
|
12
|
+
plat.name('azure').in_family('cloud')
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,578 @@
|
|
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
|
+
# master family
|
16
|
+
plat.family('os').detect { true }
|
17
|
+
|
18
|
+
plat.family('windows').in_family('os')
|
19
|
+
.detect {
|
20
|
+
if winrm? || (@backend.local? && ruby_host_os(/mswin|mingw32|windows/))
|
21
|
+
true
|
22
|
+
end
|
23
|
+
}
|
24
|
+
# windows platform
|
25
|
+
plat.name('windows').in_family('windows')
|
26
|
+
.detect {
|
27
|
+
true if detect_windows == true
|
28
|
+
}
|
29
|
+
|
30
|
+
# unix master family
|
31
|
+
plat.family('unix').in_family('os')
|
32
|
+
.detect {
|
33
|
+
# we want to catch a special case here where cisco commands
|
34
|
+
# don't return an exit status and still print to stdout
|
35
|
+
if unix_uname_s =~ /./ && !unix_uname_s.start_with?('Line has invalid autocommand ') && !unix_uname_s.start_with?('The command you have entered')
|
36
|
+
@platform[:arch] = unix_uname_m
|
37
|
+
true
|
38
|
+
end
|
39
|
+
}
|
40
|
+
|
41
|
+
# linux master family
|
42
|
+
plat.family('linux').in_family('unix')
|
43
|
+
.detect {
|
44
|
+
true if unix_uname_s =~ /linux/i
|
45
|
+
}
|
46
|
+
|
47
|
+
# debian family
|
48
|
+
plat.family('debian').in_family('linux')
|
49
|
+
.detect {
|
50
|
+
true unless unix_file_contents('/etc/debian_version').nil?
|
51
|
+
}
|
52
|
+
plat.name('ubuntu').title('Ubuntu Linux').in_family('debian')
|
53
|
+
.detect {
|
54
|
+
lsb = read_linux_lsb
|
55
|
+
if lsb && lsb[:id] =~ /ubuntu/i
|
56
|
+
@platform[:release] = lsb[:release]
|
57
|
+
true
|
58
|
+
end
|
59
|
+
}
|
60
|
+
plat.name('linuxmint').title('LinuxMint').in_family('debian')
|
61
|
+
.detect {
|
62
|
+
lsb = read_linux_lsb
|
63
|
+
if lsb && lsb[:id] =~ /linuxmint/i
|
64
|
+
@platform[:release] = lsb[:release]
|
65
|
+
true
|
66
|
+
end
|
67
|
+
}
|
68
|
+
plat.name('raspbian').title('Raspbian Linux').in_family('debian')
|
69
|
+
.detect {
|
70
|
+
if (linux_os_release && linux_os_release['NAME'] =~ /raspbian/i) || \
|
71
|
+
unix_file_exist?('/usr/bin/raspi-config')
|
72
|
+
@platform[:release] = unix_file_contents('/etc/debian_version').chomp
|
73
|
+
true
|
74
|
+
end
|
75
|
+
}
|
76
|
+
plat.name('debian').title('Debian Linux').in_family('debian')
|
77
|
+
.detect {
|
78
|
+
lsb = read_linux_lsb
|
79
|
+
if lsb && lsb[:id] =~ /debian/i
|
80
|
+
@platform[:release] = lsb[:release]
|
81
|
+
true
|
82
|
+
end
|
83
|
+
|
84
|
+
# if we get this far we have to be some type of debian
|
85
|
+
@platform[:release] = unix_file_contents('/etc/debian_version').chomp
|
86
|
+
true
|
87
|
+
}
|
88
|
+
|
89
|
+
# fedora family
|
90
|
+
plat.family('fedora').in_family('linux')
|
91
|
+
.detect {
|
92
|
+
true if linux_os_release && linux_os_release['NAME'] =~ /fedora/i
|
93
|
+
}
|
94
|
+
plat.name('fedora').title('Fedora').in_family('fedora')
|
95
|
+
.detect {
|
96
|
+
@platform[:release] = linux_os_release['VERSION_ID']
|
97
|
+
true
|
98
|
+
}
|
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
|
+
|
125
|
+
# redhat family
|
126
|
+
plat.family('redhat').in_family('linux')
|
127
|
+
.detect {
|
128
|
+
# I am not sure this returns true for all redhats in this family
|
129
|
+
# for now we are going to just try each platform
|
130
|
+
# return true unless unix_file_contents('/etc/redhat-release').nil?
|
131
|
+
|
132
|
+
true
|
133
|
+
}
|
134
|
+
plat.name('centos').title('Centos Linux').in_family('redhat')
|
135
|
+
.detect {
|
136
|
+
lsb = read_linux_lsb
|
137
|
+
if lsb && lsb[:id] =~ /centos/i
|
138
|
+
@platform[:release] = lsb[:release]
|
139
|
+
true
|
140
|
+
elsif linux_os_release && linux_os_release['NAME'] =~ /centos/i
|
141
|
+
@platform[:release] = redhatish_version(unix_file_contents('/etc/redhat-release'))
|
142
|
+
true
|
143
|
+
end
|
144
|
+
}
|
145
|
+
plat.name('oracle').title('Oracle Linux').in_family('redhat')
|
146
|
+
.detect {
|
147
|
+
if !(raw = unix_file_contents('/etc/oracle-release')).nil?
|
148
|
+
@platform[:release] = redhatish_version(raw)
|
149
|
+
true
|
150
|
+
elsif !(raw = unix_file_contents('/etc/enterprise-release')).nil?
|
151
|
+
@platform[:release] = redhatish_version(raw)
|
152
|
+
true
|
153
|
+
end
|
154
|
+
}
|
155
|
+
plat.name('scientific').title('Scientific Linux').in_family('redhat')
|
156
|
+
.detect {
|
157
|
+
lsb = read_linux_lsb
|
158
|
+
if lsb && lsb[:id] =~ /scientific/i
|
159
|
+
@platform[:release] = lsb[:release]
|
160
|
+
true
|
161
|
+
end
|
162
|
+
}
|
163
|
+
plat.name('xenserver').title('Xenserer Linux').in_family('redhat')
|
164
|
+
.detect {
|
165
|
+
lsb = read_linux_lsb
|
166
|
+
if lsb && lsb[:id] =~ /xenserver/i
|
167
|
+
@platform[:release] = lsb[:release]
|
168
|
+
true
|
169
|
+
end
|
170
|
+
}
|
171
|
+
plat.name('parallels-release').title('Parallels Linux').in_family('redhat')
|
172
|
+
.detect {
|
173
|
+
if !(raw = unix_file_contents('/etc/parallels-release')).nil?
|
174
|
+
@platform[:name] = redhatish_platform(raw)
|
175
|
+
@platform[:release] = raw[/(\d\.\d\.\d)/, 1]
|
176
|
+
true
|
177
|
+
end
|
178
|
+
}
|
179
|
+
plat.name('wrlinux').title('Wind River Linux').in_family('redhat')
|
180
|
+
.detect {
|
181
|
+
if linux_os_release && linux_os_release['ID_LIKE'] =~ /wrlinux/i
|
182
|
+
@platform[:release] = linux_os_release['VERSION']
|
183
|
+
true
|
184
|
+
end
|
185
|
+
}
|
186
|
+
plat.name('amazon').title('Amazon Linux').in_family('redhat')
|
187
|
+
.detect {
|
188
|
+
lsb = read_linux_lsb
|
189
|
+
if lsb && lsb[:id] =~ /amazon/i
|
190
|
+
@platform[:release] = lsb[:release]
|
191
|
+
true
|
192
|
+
elsif (raw = unix_file_contents('/etc/system-release')) =~ /amazon/i
|
193
|
+
@platform[:name] = redhatish_platform(raw)
|
194
|
+
@platform[:release] = redhatish_version(raw)
|
195
|
+
true
|
196
|
+
end
|
197
|
+
}
|
198
|
+
plat.name('cloudlinux').title('CloudLinux').in_family('redhat')
|
199
|
+
.detect {
|
200
|
+
lsb = read_linux_lsb
|
201
|
+
if lsb && lsb[:id] =~ /cloudlinux/i
|
202
|
+
@platform[:release] = lsb[:release]
|
203
|
+
true
|
204
|
+
elsif (raw = unix_file_contents('/etc/redhat-release')) =~ /cloudlinux/i
|
205
|
+
@platform[:name] = redhatish_platform(raw)
|
206
|
+
@platform[:release] = redhatish_version(raw)
|
207
|
+
true
|
208
|
+
end
|
209
|
+
}
|
210
|
+
# keep redhat at the end as a fallback for anything with a redhat-release
|
211
|
+
plat.name('redhat').title('Red Hat Linux').in_family('redhat')
|
212
|
+
.detect {
|
213
|
+
lsb = read_linux_lsb
|
214
|
+
if lsb && lsb[:id] =~ /redhat/i
|
215
|
+
@platform[:release] = lsb[:release]
|
216
|
+
true
|
217
|
+
elsif !(raw = unix_file_contents('/etc/redhat-release')).nil?
|
218
|
+
# must be some type of redhat
|
219
|
+
@platform[:name] = redhatish_platform(raw)
|
220
|
+
@platform[:release] = redhatish_version(raw)
|
221
|
+
true
|
222
|
+
end
|
223
|
+
}
|
224
|
+
|
225
|
+
# suse family
|
226
|
+
plat.family('suse').in_family('linux')
|
227
|
+
.detect {
|
228
|
+
if !(suse = unix_file_contents('/etc/SuSE-release')).nil?
|
229
|
+
version = suse.scan(/VERSION = (\d+)\nPATCHLEVEL = (\d+)/).flatten.join('.')
|
230
|
+
version = suse[/VERSION = ([\d\.]{2,})/, 1] if version == ''
|
231
|
+
@platform[:release] = version
|
232
|
+
true
|
233
|
+
end
|
234
|
+
}
|
235
|
+
plat.name('opensuse').title('OpenSUSE Linux').in_family('suse')
|
236
|
+
.detect {
|
237
|
+
true if unix_file_contents('/etc/SuSE-release') =~ /^opensuse/i
|
238
|
+
}
|
239
|
+
plat.name('suse').title('Suse Linux').in_family('suse')
|
240
|
+
.detect {
|
241
|
+
true if unix_file_contents('/etc/SuSE-release') =~ /suse/i
|
242
|
+
}
|
243
|
+
|
244
|
+
# arch
|
245
|
+
plat.name('arch').title('Arch Linux').in_family('linux')
|
246
|
+
.detect {
|
247
|
+
if !unix_file_contents('/etc/arch-release').nil?
|
248
|
+
# Because this is a rolling release distribution,
|
249
|
+
# use the kernel release, ex. 4.1.6-1-ARCH
|
250
|
+
@platform[:release] = unix_uname_r
|
251
|
+
true
|
252
|
+
end
|
253
|
+
}
|
254
|
+
|
255
|
+
# slackware
|
256
|
+
plat.name('slackware').title('Slackware Linux').in_family('linux')
|
257
|
+
.detect {
|
258
|
+
if !(raw = unix_file_contents('/etc/slackware-version')).nil?
|
259
|
+
@platform[:release] = raw.scan(/(\d+|\.+)/).join
|
260
|
+
true
|
261
|
+
end
|
262
|
+
}
|
263
|
+
|
264
|
+
# gentoo
|
265
|
+
plat.name('gentoo').title('Gentoo Linux').in_family('linux')
|
266
|
+
.detect {
|
267
|
+
if !(raw = unix_file_contents('/etc/gentoo-release')).nil?
|
268
|
+
@platform[:release] = raw.scan(/(\d+|\.+)/).join
|
269
|
+
true
|
270
|
+
end
|
271
|
+
}
|
272
|
+
|
273
|
+
# exherbo
|
274
|
+
plat.name('exherbo').title('Exherbo Linux').in_family('linux')
|
275
|
+
.detect {
|
276
|
+
unless unix_file_contents('/etc/exherbo-release').nil?
|
277
|
+
# Because this is a rolling release distribution,
|
278
|
+
# use the kernel release, ex. 4.1.6
|
279
|
+
@platform[:release] = unix_uname_r
|
280
|
+
true
|
281
|
+
end
|
282
|
+
}
|
283
|
+
|
284
|
+
# alpine
|
285
|
+
plat.name('alpine').title('Alpine Linux').in_family('linux')
|
286
|
+
.detect {
|
287
|
+
if !(raw = unix_file_contents('/etc/alpine-release')).nil?
|
288
|
+
@platform[:release] = raw.strip
|
289
|
+
true
|
290
|
+
end
|
291
|
+
}
|
292
|
+
|
293
|
+
# coreos
|
294
|
+
plat.name('coreos').title('CoreOS Linux').in_family('linux')
|
295
|
+
.detect {
|
296
|
+
unless unix_file_contents('/etc/coreos/update.conf').nil?
|
297
|
+
lsb = read_linux_lsb
|
298
|
+
@platform[:release] = lsb[:release]
|
299
|
+
true
|
300
|
+
end
|
301
|
+
}
|
302
|
+
|
303
|
+
# brocade family detected here if device responds to 'uname' command,
|
304
|
+
# happens when logging in as root
|
305
|
+
plat.family('brocade').title('Brocade Family').in_family('linux')
|
306
|
+
.detect {
|
307
|
+
!brocade_version.nil?
|
308
|
+
}
|
309
|
+
|
310
|
+
# genaric linux
|
311
|
+
# this should always be last in the linux family list
|
312
|
+
plat.name('linux').title('Genaric Linux').in_family('linux')
|
313
|
+
.detect {
|
314
|
+
true
|
315
|
+
}
|
316
|
+
|
317
|
+
# openvms
|
318
|
+
plat.name('openvms').title('OpenVMS').in_family('unix')
|
319
|
+
.detect {
|
320
|
+
if unix_uname_s =~ /unrecognized command verb/i
|
321
|
+
cmd = @backend.run_command('show system/noprocess')
|
322
|
+
unless cmd.exit_status != 0 || cmd.stdout.empty?
|
323
|
+
@platform[:name] = cmd.stdout.downcase.split(' ')[0]
|
324
|
+
cmd = @backend.run_command('write sys$output f$getsyi("VERSION")')
|
325
|
+
@platform[:release] = cmd.stdout.downcase.split("\n")[1][1..-1]
|
326
|
+
cmd = @backend.run_command('write sys$output f$getsyi("ARCH_NAME")')
|
327
|
+
@platform[:arch] = cmd.stdout.downcase.split("\n")[1]
|
328
|
+
true
|
329
|
+
end
|
330
|
+
end
|
331
|
+
}
|
332
|
+
|
333
|
+
# aix
|
334
|
+
plat.family('aix').in_family('unix')
|
335
|
+
.detect {
|
336
|
+
true if unix_uname_s =~ /aix/i
|
337
|
+
}
|
338
|
+
plat.name('aix').title('Aix').in_family('aix')
|
339
|
+
.detect {
|
340
|
+
out = @backend.run_command('uname -rvp').stdout
|
341
|
+
m = out.match(/(\d+)\s+(\d+)\s+(.*)/)
|
342
|
+
unless m.nil?
|
343
|
+
@platform[:release] = "#{m[2]}.#{m[1]}"
|
344
|
+
@platform[:arch] = m[3].to_s
|
345
|
+
end
|
346
|
+
true
|
347
|
+
}
|
348
|
+
|
349
|
+
# solaris family
|
350
|
+
plat.family('solaris').in_family('unix')
|
351
|
+
.detect {
|
352
|
+
if unix_uname_s =~ /sunos/i
|
353
|
+
unless (version = /^5\.(?<release>\d+)$/.match(unix_uname_r)).nil?
|
354
|
+
@platform[:release] = version['release']
|
355
|
+
end
|
356
|
+
|
357
|
+
arch = @backend.run_command('uname -p')
|
358
|
+
@platform[:arch] = arch.stdout.chomp if arch.exit_status == 0
|
359
|
+
true
|
360
|
+
end
|
361
|
+
}
|
362
|
+
plat.name('smartos').title('SmartOS').in_family('solaris')
|
363
|
+
.detect {
|
364
|
+
rel = unix_file_contents('/etc/release')
|
365
|
+
if /^.*(SmartOS).*$/ =~ rel
|
366
|
+
true
|
367
|
+
end
|
368
|
+
}
|
369
|
+
plat.name('omnios').title('Omnios').in_family('solaris')
|
370
|
+
.detect {
|
371
|
+
rel = unix_file_contents('/etc/release')
|
372
|
+
if !(m = /^\s*(OmniOS).*r(\d+).*$/.match(rel)).nil?
|
373
|
+
@platform[:release] = m[2]
|
374
|
+
true
|
375
|
+
end
|
376
|
+
}
|
377
|
+
plat.name('openindiana').title('Openindiana').in_family('solaris')
|
378
|
+
.detect {
|
379
|
+
rel = unix_file_contents('/etc/release')
|
380
|
+
if !(m = /^\s*(OpenIndiana).*oi_(\d+).*$/.match(rel)).nil?
|
381
|
+
@platform[:release] = m[2]
|
382
|
+
true
|
383
|
+
end
|
384
|
+
}
|
385
|
+
plat.name('opensolaris').title('Open Solaris').in_family('solaris')
|
386
|
+
.detect {
|
387
|
+
rel = unix_file_contents('/etc/release')
|
388
|
+
if !(m = /^\s*(OpenSolaris).*snv_(\d+).*$/.match(rel)).nil?
|
389
|
+
@platform[:release] = m[2]
|
390
|
+
true
|
391
|
+
end
|
392
|
+
}
|
393
|
+
plat.name('nexentacore').title('Nexentacore').in_family('solaris')
|
394
|
+
.detect {
|
395
|
+
rel = unix_file_contents('/etc/release')
|
396
|
+
if /^\s*(NexentaCore)\s.*$/ =~ rel
|
397
|
+
true
|
398
|
+
end
|
399
|
+
}
|
400
|
+
plat.name('solaris').title('Solaris').in_family('solaris')
|
401
|
+
.detect {
|
402
|
+
rel = unix_file_contents('/etc/release')
|
403
|
+
if !(m = /Oracle Solaris (\d+)/.match(rel)).nil?
|
404
|
+
# TODO: should be string!
|
405
|
+
@platform[:release] = m[1]
|
406
|
+
true
|
407
|
+
elsif /^\s*(Solaris)\s.*$/ =~ rel
|
408
|
+
true
|
409
|
+
end
|
410
|
+
|
411
|
+
# must be some unknown solaris
|
412
|
+
true
|
413
|
+
}
|
414
|
+
|
415
|
+
# hpux
|
416
|
+
plat.family('hpux').in_family('unix')
|
417
|
+
.detect {
|
418
|
+
true if unix_uname_s =~ /hp-ux/i
|
419
|
+
}
|
420
|
+
plat.name('hpux').title('Hpux').in_family('hpux')
|
421
|
+
.detect {
|
422
|
+
@platform[:release] = unix_uname_r.lines[0].chomp
|
423
|
+
true
|
424
|
+
}
|
425
|
+
|
426
|
+
# qnx
|
427
|
+
plat.family('qnx').in_family('unix')
|
428
|
+
.detect {
|
429
|
+
true if unix_uname_s =~ /qnx/i
|
430
|
+
}
|
431
|
+
plat.name('qnx').title('QNX').in_family('qnx')
|
432
|
+
.detect {
|
433
|
+
@platform[:name] = unix_uname_s.lines[0].chomp.downcase
|
434
|
+
@platform[:release] = unix_uname_r.lines[0].chomp
|
435
|
+
@platform[:arch] = unix_uname_m
|
436
|
+
true
|
437
|
+
}
|
438
|
+
|
439
|
+
# bsd family
|
440
|
+
plat.family('bsd').in_family('unix')
|
441
|
+
.detect {
|
442
|
+
# we need a better way to determin this family
|
443
|
+
# for now we are going to just try each platform
|
444
|
+
true
|
445
|
+
}
|
446
|
+
plat.family('darwin').in_family('bsd')
|
447
|
+
.detect {
|
448
|
+
if unix_uname_s =~ /darwin/i
|
449
|
+
cmd = unix_file_contents('/usr/bin/sw_vers')
|
450
|
+
unless cmd.nil?
|
451
|
+
m = cmd.match(/^ProductVersion:\s+(.+)$/)
|
452
|
+
@platform[:release] = m.nil? ? nil : m[1]
|
453
|
+
m = cmd.match(/^BuildVersion:\s+(.+)$/)
|
454
|
+
@platform[:build] = m.nil? ? nil : m[1]
|
455
|
+
end
|
456
|
+
@platform[:release] = unix_uname_r.lines[0].chomp if @platform[:release].nil?
|
457
|
+
@platform[:arch] = unix_uname_m
|
458
|
+
true
|
459
|
+
end
|
460
|
+
}
|
461
|
+
plat.name('mac_os_x').title('macOS X').in_family('darwin')
|
462
|
+
.detect {
|
463
|
+
cmd = unix_file_contents('/System/Library/CoreServices/SystemVersion.plist')
|
464
|
+
@platform[:uuid_command] = "system_profiler SPHardwareDataType | awk '/UUID/ { print $3; }'"
|
465
|
+
true if cmd =~ /Mac OS X/i
|
466
|
+
}
|
467
|
+
plat.name('darwin').title('Darwin').in_family('darwin')
|
468
|
+
.detect {
|
469
|
+
# must be some other type of darwin
|
470
|
+
@platform[:name] = unix_uname_s.lines[0].chomp
|
471
|
+
true
|
472
|
+
}
|
473
|
+
plat.name('freebsd').title('Freebsd').in_family('bsd')
|
474
|
+
.detect {
|
475
|
+
if unix_uname_s =~ /freebsd/i
|
476
|
+
@platform[:name] = unix_uname_s.lines[0].chomp
|
477
|
+
@platform[:release] = unix_uname_r.lines[0].chomp
|
478
|
+
true
|
479
|
+
end
|
480
|
+
}
|
481
|
+
plat.name('openbsd').title('Openbsd').in_family('bsd')
|
482
|
+
.detect {
|
483
|
+
if unix_uname_s =~ /openbsd/i
|
484
|
+
@platform[:name] = unix_uname_s.lines[0].chomp
|
485
|
+
@platform[:release] = unix_uname_r.lines[0].chomp
|
486
|
+
true
|
487
|
+
end
|
488
|
+
}
|
489
|
+
plat.name('netbsd').title('Netbsd').in_family('bsd')
|
490
|
+
.detect {
|
491
|
+
if unix_uname_s =~ /netbsd/i
|
492
|
+
@platform[:name] = unix_uname_s.lines[0].chomp
|
493
|
+
@platform[:release] = unix_uname_r.lines[0].chomp
|
494
|
+
true
|
495
|
+
end
|
496
|
+
}
|
497
|
+
|
498
|
+
# arista_eos family
|
499
|
+
plat.family('arista_eos').title('Arista EOS Family').in_family('os')
|
500
|
+
.detect {
|
501
|
+
true
|
502
|
+
}
|
503
|
+
plat.name('arista_eos').title('Arista EOS').in_family('arista_eos')
|
504
|
+
.detect {
|
505
|
+
cmd = @backend.run_command('show version | json')
|
506
|
+
if cmd.exit_status == 0 && !cmd.stdout.empty?
|
507
|
+
require 'json'
|
508
|
+
begin
|
509
|
+
eos_ver = JSON.parse(cmd.stdout)
|
510
|
+
@platform[:release] = eos_ver['version']
|
511
|
+
@platform[:arch] = eos_ver['architecture']
|
512
|
+
true
|
513
|
+
rescue JSON::ParserError
|
514
|
+
nil
|
515
|
+
end
|
516
|
+
end
|
517
|
+
}
|
518
|
+
|
519
|
+
# esx
|
520
|
+
plat.family('esx').title('ESXi Family').in_family('os')
|
521
|
+
.detect {
|
522
|
+
true if unix_uname_s =~ /vmkernel/i
|
523
|
+
}
|
524
|
+
plat.name('vmkernel').in_family('esx')
|
525
|
+
.detect {
|
526
|
+
@platform[:name] = unix_uname_s.lines[0].chomp
|
527
|
+
@platform[:release] = unix_uname_r.lines[0].chomp
|
528
|
+
true
|
529
|
+
}
|
530
|
+
|
531
|
+
# cisco_ios family
|
532
|
+
plat.family('cisco').title('Cisco Family').in_family('os')
|
533
|
+
.detect {
|
534
|
+
!cisco_show_version.nil?
|
535
|
+
}
|
536
|
+
plat.name('cisco_ios').title('Cisco IOS').in_family('cisco')
|
537
|
+
.detect {
|
538
|
+
v = cisco_show_version
|
539
|
+
next unless v[:type] == 'ios'
|
540
|
+
@platform[:release] = v[:version]
|
541
|
+
@platform[:arch] = nil
|
542
|
+
true
|
543
|
+
}
|
544
|
+
plat.name('cisco_ios_xe').title('Cisco IOS XE').in_family('cisco')
|
545
|
+
.detect {
|
546
|
+
v = cisco_show_version
|
547
|
+
next unless v[:type] == 'ios-xe'
|
548
|
+
@platform[:release] = v[:version]
|
549
|
+
@platform[:arch] = nil
|
550
|
+
true
|
551
|
+
}
|
552
|
+
plat.name('cisco_nexus').title('Cisco Nexus').in_family('cisco')
|
553
|
+
.detect {
|
554
|
+
v = cisco_show_version
|
555
|
+
next unless v[:type] == 'nexus'
|
556
|
+
@platform[:release] = v[:version]
|
557
|
+
@platform[:arch] = nil
|
558
|
+
@platform[:uuid_command] = 'show inventory chassis | include SN'
|
559
|
+
true
|
560
|
+
}
|
561
|
+
|
562
|
+
# brocade family
|
563
|
+
plat.family('brocade').title('Brocade Family').in_family('os')
|
564
|
+
.detect {
|
565
|
+
!brocade_version.nil?
|
566
|
+
}
|
567
|
+
|
568
|
+
plat.name('brocade_fos').title('Brocade FOS').in_family('brocade')
|
569
|
+
.detect {
|
570
|
+
v = brocade_version
|
571
|
+
next unless v[:type] == 'fos'
|
572
|
+
@platform[:release] = v[:version]
|
573
|
+
@platform[:arch] = nil
|
574
|
+
true
|
575
|
+
}
|
576
|
+
end
|
577
|
+
end
|
578
|
+
end
|