train 3.2.14 → 3.2.20

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 (42) hide show
  1. checksums.yaml +4 -4
  2. metadata +29 -149
  3. data/LICENSE +0 -201
  4. data/lib/train.rb +0 -193
  5. data/lib/train/errors.rb +0 -44
  6. data/lib/train/extras.rb +0 -11
  7. data/lib/train/extras/command_wrapper.rb +0 -201
  8. data/lib/train/extras/stat.rb +0 -136
  9. data/lib/train/file.rb +0 -212
  10. data/lib/train/file/local.rb +0 -82
  11. data/lib/train/file/local/unix.rb +0 -96
  12. data/lib/train/file/local/windows.rb +0 -68
  13. data/lib/train/file/remote.rb +0 -40
  14. data/lib/train/file/remote/aix.rb +0 -29
  15. data/lib/train/file/remote/linux.rb +0 -21
  16. data/lib/train/file/remote/qnx.rb +0 -41
  17. data/lib/train/file/remote/unix.rb +0 -110
  18. data/lib/train/file/remote/windows.rb +0 -110
  19. data/lib/train/globals.rb +0 -5
  20. data/lib/train/options.rb +0 -81
  21. data/lib/train/platforms.rb +0 -102
  22. data/lib/train/platforms/common.rb +0 -34
  23. data/lib/train/platforms/detect.rb +0 -12
  24. data/lib/train/platforms/detect/helpers/os_common.rb +0 -160
  25. data/lib/train/platforms/detect/helpers/os_linux.rb +0 -80
  26. data/lib/train/platforms/detect/helpers/os_windows.rb +0 -142
  27. data/lib/train/platforms/detect/scanner.rb +0 -85
  28. data/lib/train/platforms/detect/specifications/api.rb +0 -20
  29. data/lib/train/platforms/detect/specifications/os.rb +0 -629
  30. data/lib/train/platforms/detect/uuid.rb +0 -32
  31. data/lib/train/platforms/family.rb +0 -31
  32. data/lib/train/platforms/platform.rb +0 -109
  33. data/lib/train/plugin_test_helper.rb +0 -51
  34. data/lib/train/plugins.rb +0 -40
  35. data/lib/train/plugins/base_connection.rb +0 -198
  36. data/lib/train/plugins/transport.rb +0 -49
  37. data/lib/train/transports/cisco_ios_connection.rb +0 -133
  38. data/lib/train/transports/local.rb +0 -240
  39. data/lib/train/transports/mock.rb +0 -183
  40. data/lib/train/transports/ssh.rb +0 -271
  41. data/lib/train/transports/ssh_connection.rb +0 -342
  42. data/lib/train/version.rb +0 -7
@@ -1,102 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require_relative "platforms/common"
4
- require_relative "platforms/detect"
5
- require_relative "platforms/detect/scanner"
6
- require_relative "platforms/detect/specifications/os"
7
- require_relative "platforms/detect/specifications/api"
8
- require_relative "platforms/detect/uuid"
9
- require_relative "platforms/family"
10
- require_relative "platforms/platform"
11
-
12
- module Train::Platforms
13
- # Retrieve the current platform list
14
- #
15
- # @return [Hash] map with platform names and their objects
16
- def self.list
17
- @list ||= {}
18
- end
19
-
20
- # Retrieve the current family list
21
- #
22
- # @return [Hash] map with family names and their objects
23
- def self.families
24
- @families ||= {}
25
- end
26
-
27
- # Clear all platform settings. Only used for testing.
28
- def self.__reset
29
- @list = {}
30
- @families = {}
31
- end
32
-
33
- # Create or update a platform
34
- #
35
- # @return Train::Platform
36
- def self.name(name, condition = {})
37
- # TODO: refactor this against family. They're stupidly similar
38
- # Check the list to see if one is already created
39
- plat = list[name]
40
- unless plat.nil?
41
- # Pass the condition incase we are adding a family relationship
42
- plat.condition = condition unless condition.nil?
43
- return plat
44
- end
45
-
46
- Train::Platforms::Platform.new(name, condition)
47
- end
48
-
49
- # Create or update a family
50
- #
51
- # @return Train::Platforms::Family
52
- def self.family(name, condition = {})
53
- # Check the families to see if one is already created
54
- family = families[name]
55
- unless family.nil?
56
- # Pass the condition incase we are adding a family relationship
57
- family.condition = condition unless condition.nil?
58
- return family
59
- end
60
-
61
- Train::Platforms::Family.new(name, condition)
62
- end
63
-
64
- # Find the families or top level platforms
65
- #
66
- # @return [Hash] with top level family and platforms
67
- def self.top_platforms
68
- empty_list = list.select { |_key, value| value.families.empty? }
69
- empty_fams = families.select { |_key, value| value.families.empty? }
70
-
71
- empty_list.merge empty_fams
72
- end
73
-
74
- # List all platforms and families in a readable output
75
- def self.list_all
76
- top_platforms = self.top_platforms
77
- top_platforms.each_value do |platform|
78
- puts platform.title
79
- print_children(platform) if defined?(platform.children)
80
- end
81
- end
82
-
83
- def self.print_children(parent, pad = 2)
84
- parent.children.each do |key, value|
85
- obj = key
86
- puts "#{" " * pad}-> #{obj.title}#{value unless value.empty?}"
87
- print_children(obj, pad + 2) if defined?(obj.children) && !obj.children.nil?
88
- end
89
- end
90
-
91
- def self.export
92
- export = []
93
- list.each do |name, platform|
94
- platform.find_family_hierarchy
95
- export << {
96
- name: name,
97
- families: platform.family_hierarchy,
98
- }
99
- end
100
- export.sort_by { |platform| platform[:name] }
101
- end
102
- end
@@ -1,34 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module Train::Platforms
4
- module Common
5
- # Add a family connection. This will create a family
6
- # if it does not exist and add a child relationship.
7
- def in_family(family)
8
- if self.class == Train::Platforms::Family && @name == family
9
- raise "Unable to add family #{@name} to itself: '#{@name}.in_family(#{family})'"
10
- end
11
-
12
- # add family to the family list
13
- family = Train::Platforms.family(family)
14
- family.children[self] = @condition
15
-
16
- @families[family] = @condition
17
- @condition = nil
18
- self
19
- end
20
-
21
- def detect(&block)
22
- if block_given?
23
- @detect = block
24
- self
25
- elsif @detect.nil?
26
- # we are returning a block that just returns false here
27
- # to skip the family/platform evaluation if detect is not set
28
- ->(_) { false }
29
- else
30
- @detect
31
- end
32
- end
33
- end
34
- end
@@ -1,12 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module Train::Platforms
4
- module Detect
5
- # Main detect method to scan all platforms for a match
6
- #
7
- # @return Train::Platform instance or error if none found
8
- def self.scan(backend)
9
- Scanner.new(backend).scan
10
- end
11
- end
12
- end
@@ -1,160 +0,0 @@
1
- require_relative "os_linux"
2
- require_relative "os_windows"
3
- require "rbconfig"
4
-
5
- module Train::Platforms::Detect::Helpers
6
- module OSCommon
7
- include Train::Platforms::Detect::Helpers::Linux
8
- include Train::Platforms::Detect::Helpers::Windows
9
-
10
- def ruby_host_os(regex)
11
- ::RbConfig::CONFIG["host_os"] =~ regex
12
- end
13
-
14
- def winrm?
15
- backend_name == "TrainPlugins::WinRM::Connection"
16
- end
17
-
18
- def backend_name
19
- @backend.class.name
20
- end
21
-
22
- def unix_file_contents(path)
23
- # keep a log of files incase multiple checks call the same one
24
- return @files[path] if @files.key?(path)
25
-
26
- res = @backend.run_command("test -f #{path} && cat #{path}")
27
- # ignore files that can't be read
28
- @files[path] = res.exit_status == 0 ? res.stdout : nil
29
- @files[path]
30
- end
31
-
32
- def unix_file_exist?(path)
33
- @backend.run_command("test -f #{path}").exit_status == 0
34
- end
35
-
36
- def command_output(cmd)
37
- res = @backend.run_command(cmd).stdout
38
- # When you try to execute command using ssh connction as root user and you have provided ssh user identity file
39
- # it gives standard output to login as authorised user other than root. To show this standard ouput as an error
40
- # to user we are matching the string of stdout and raising the error here so that user gets exact information.
41
- if @backend.class.to_s == "Train::Transports::SSH::Connection" && res =~ /Please login as the user/
42
- raise Train::UserError, "SSH failed: #{res}"
43
- end
44
-
45
- res.strip! unless res.nil?
46
- res
47
- end
48
-
49
- def unix_uname_s
50
- return @uname[:s] if @uname.key?(:s)
51
-
52
- @uname[:s] = command_output("uname -s")
53
- end
54
-
55
- def unix_uname_r
56
- return @uname[:r] if @uname.key?(:r)
57
-
58
- @uname[:r] = command_output("uname -r")
59
- end
60
-
61
- def unix_uname_m
62
- return @uname[:m] if @uname.key?(:m)
63
-
64
- @uname[:m] = command_output("uname -m")
65
- end
66
-
67
- def brocade_version
68
- return @cache[:brocade] if @cache.key?(:brocade)
69
-
70
- res = command_output("version")
71
-
72
- m = res.match(/^Fabric OS:\s+v(\S+)$/)
73
- unless m.nil?
74
- return @cache[:brocade] = { version: m[1], type: "fos" }
75
- end
76
-
77
- @cache[:brocade] = nil
78
- end
79
-
80
- def cisco_show_version
81
- return @cache[:cisco] if @cache.key?(:cisco)
82
-
83
- res = command_output("show version")
84
-
85
- m = res.match(/Cisco IOS Software, [^,]+? \(([^,]+?)\), Version (\d+\.\d+)/)
86
- unless m.nil?
87
- return @cache[:cisco] = { version: m[2], model: m[1], type: "ios" }
88
- end
89
-
90
- m = res.match(/Cisco IOS Software, IOS-XE Software, [^,]+? \(([^,]+?)\), Version (\d+\.\d+\.\d+[A-Z]*)/)
91
- unless m.nil?
92
- return @cache[:cisco] = { version: m[2], model: m[1], type: "ios-xe" }
93
- end
94
-
95
- m = res.match(/Cisco Nexus Operating System \(NX-OS\) Software/)
96
- unless m.nil?
97
- v = res[/^\s*system:\s+version (\d+\.\d+)/, 1]
98
- return @cache[:cisco] = { version: v, type: "nexus" }
99
- end
100
-
101
- @cache[:cisco] = nil
102
- end
103
-
104
- def unix_uuid
105
- (unix_uuid_from_chef ||
106
- unix_uuid_from_machine_file ||
107
- uuid_from_command ||
108
- raise(Train::TransportError, "Cannot find a UUID for your node."))
109
- end
110
-
111
- def unix_uuid_from_chef
112
- file = @backend.file("/var/chef/cache/data_collector_metadata.json")
113
- if file.exist? && file.size != 0
114
- json = ::JSON.parse(file.content)
115
- return json["node_uuid"] if json["node_uuid"]
116
- end
117
- end
118
-
119
- def unix_uuid_from_machine_file
120
- # require 'pry';binding.pry
121
- %W{
122
- /etc/chef/chef_guid
123
- #{ENV["HOME"]}/.chef/chef_guid
124
- /etc/machine-id
125
- /var/lib/dbus/machine-id
126
- /var/db/dbus/machine-id
127
- }.each do |path|
128
- file = @backend.file(path)
129
- next unless file.exist? && file.size != 0
130
- return file.content.chomp if path =~ /guid/
131
-
132
- return uuid_from_string(file.content.chomp)
133
- end
134
- nil
135
- end
136
-
137
- # This takes a command from the platform detect block to run.
138
- # We expect the command to return a unique identifier which
139
- # we turn into a UUID.
140
- def uuid_from_command
141
- return unless @platform[:uuid_command]
142
-
143
- result = @backend.run_command(@platform[:uuid_command])
144
- uuid_from_string(result.stdout.chomp) if result.exit_status == 0 && !result.stdout.empty?
145
- end
146
-
147
- # This hashes the passed string into SHA1.
148
- # Then it downgrades the 160bit SHA1 to a 128bit
149
- # then we format it as a valid UUIDv5.
150
- def uuid_from_string(string)
151
- hash = Digest::SHA1.new
152
- hash.update(string)
153
- ary = hash.digest.unpack("NnnnnN")
154
- ary[2] = (ary[2] & 0x0FFF) | (5 << 12)
155
- ary[3] = (ary[3] & 0x3FFF) | 0x8000
156
- # rubocop:disable Style/FormatString
157
- "%08x-%04x-%04x-%04x-%04x%08x" % ary
158
- end
159
- end
160
- end
@@ -1,80 +0,0 @@
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 /Amazon Linux/i
14
- /([\d\.]+)/.match(conf)[1]
15
- when /derived from .*linux|amazon/i
16
- /Linux ((\d+|\.)+)/i.match(conf)[1]
17
- else
18
- /release ([\d\.]+)/.match(conf)[1]
19
- end
20
- end
21
-
22
- def linux_os_release
23
- data = unix_file_contents("/etc/os-release")
24
- return if data.nil?
25
-
26
- os_info = parse_os_release_info(data)
27
- cisco_info_file = os_info["CISCO_RELEASE_INFO"]
28
- if cisco_info_file
29
- os_info.merge!(parse_os_release_info(unix_file_contents(cisco_info_file)))
30
- end
31
-
32
- os_info
33
- end
34
-
35
- def parse_os_release_info(raw)
36
- return {} if raw.nil?
37
-
38
- raw.lines.each_with_object({}) do |line, memo|
39
- line.strip!
40
- next if line.nil? || line.empty?
41
- next if line.start_with?("#")
42
-
43
- key, value = line.split("=", 2)
44
- memo[key] = value.gsub(/\A"|"\Z/, "") unless value.nil? || value.empty?
45
- end
46
- end
47
-
48
- def lsb_config(content)
49
- id = /^DISTRIB_ID=["']?(.+?)["']?$/.match(content)
50
- release = /^DISTRIB_RELEASE=["']?(.+?)["']?$/.match(content)
51
- codename = /^DISTRIB_CODENAME=["']?(.+?)["']?$/.match(content)
52
- {
53
- id: id.nil? ? nil : id[1],
54
- release: release.nil? ? nil : release[1],
55
- codename: codename.nil? ? nil : codename[1],
56
- }
57
- end
58
-
59
- def lsb_release(content)
60
- id = /^Distributor ID:\s+(.+)$/.match(content)
61
- release = /^Release:\s+(.+)$/.match(content)
62
- codename = /^Codename:\s+(.+)$/.match(content)
63
- {
64
- id: id.nil? ? nil : id[1],
65
- release: release.nil? ? nil : release[1],
66
- codename: codename.nil? ? nil : codename[1],
67
- }
68
- end
69
-
70
- def read_linux_lsb
71
- return @lsb unless @lsb.empty?
72
-
73
- if !(raw = unix_file_contents("/etc/lsb-release")).nil?
74
- @lsb = lsb_config(raw)
75
- elsif !(raw = unix_file_contents("/usr/bin/lsb-release")).nil?
76
- @lsb = lsb_release(raw)
77
- end
78
- end
79
- end
80
- end
@@ -1,142 +0,0 @@
1
- module Train::Platforms::Detect::Helpers
2
- module Windows
3
- def detect_windows
4
- check_cmd || check_powershell
5
- end
6
-
7
- def check_cmd
8
- # try to detect windows, use cmd.exe to also support Microsoft OpenSSH
9
- res = @backend.run_command("cmd.exe /c ver")
10
-
11
- return false if (res.exit_status != 0) || res.stdout.empty?
12
-
13
- # if the ver contains `Windows`, we know its a Windows system
14
- version = res.stdout.strip
15
- return false unless version.downcase =~ /windows/
16
-
17
- @platform[:family] = "windows"
18
-
19
- # try to extract release from eg. `Microsoft Windows [Version 6.3.9600]`
20
- release = /\[(?<name>.*)\]/.match(version)
21
- if release[:name]
22
- # release is 6.3.9600 now
23
- @platform[:release] = release[:name].downcase.gsub("version", "").strip
24
- # fallback, if we are not able to extract the name from wmic later
25
- @platform[:name] = "Windows #{@platform[:release]}"
26
- end
27
-
28
- read_wmic
29
- true
30
- end
31
-
32
- def check_powershell
33
- command = @backend.run_command(
34
- "Get-WmiObject Win32_OperatingSystem | Select Caption,Version | ConvertTo-Json"
35
- )
36
- return false if (command.exit_status != 0) || command.stdout.empty?
37
-
38
- payload = JSON.parse(command.stdout)
39
- @platform[:family] = "windows"
40
- @platform[:release] = payload["Version"]
41
- @platform[:name] = payload["Caption"]
42
-
43
- read_wmic
44
- true
45
- end
46
-
47
- # reads os name and version from wmic
48
- # @see https://msdn.microsoft.com/en-us/library/bb742610.aspx#EEAA
49
- # Thanks to Matt Wrock (https://github.com/mwrock) for this hint
50
- def read_wmic
51
- res = @backend.run_command("wmic os get * /format:list")
52
- if res.exit_status == 0
53
- sys_info = {}
54
- res.stdout.lines.each do |line|
55
- m = /^\s*([^=]*?)\s*=\s*(.*?)\s*$/.match(line)
56
- sys_info[m[1].to_sym] = m[2] unless m.nil? || m[1].nil?
57
- end
58
-
59
- @platform[:release] = sys_info[:Version]
60
- # additional info on windows
61
- @platform[:build] = sys_info[:BuildNumber]
62
- @platform[:name] = sys_info[:Caption]
63
- @platform[:name] = @platform[:name].gsub("Microsoft", "").strip unless @platform[:name].empty?
64
- @platform[:arch] = read_wmic_cpu
65
- end
66
- end
67
-
68
- # `OSArchitecture` from `read_wmic` does not match a normal standard
69
- # For example, `x86_64` shows as `64-bit`
70
- def read_wmic_cpu
71
- res = @backend.run_command("wmic cpu get architecture /format:list")
72
- if res.exit_status == 0
73
- sys_info = {}
74
- res.stdout.lines.each do |line|
75
- m = /^\s*([^=]*?)\s*=\s*(.*?)\s*$/.match(line)
76
- sys_info[m[1].to_sym] = m[2] unless m.nil? || m[1].nil?
77
- end
78
- end
79
-
80
- # This converts `wmic os get architecture` output to a normal standard
81
- # https://msdn.microsoft.com/en-us/library/aa394373(VS.85).aspx
82
- arch_map = {
83
- 0 => "i386",
84
- 1 => "mips",
85
- 2 => "alpha",
86
- 3 => "powerpc",
87
- 5 => "arm",
88
- 6 => "ia64",
89
- 9 => "x86_64",
90
- }
91
-
92
- # The value of `wmic cpu get architecture` is always a number between 0-9
93
- arch_number = sys_info[:Architecture].to_i
94
- arch_map[arch_number]
95
- end
96
-
97
- # This method scans the target os for a unique uuid to use
98
- def windows_uuid
99
- uuid = windows_uuid_from_chef
100
- uuid = windows_uuid_from_machine_file if uuid.nil?
101
- uuid = windows_uuid_from_wmic if uuid.nil?
102
- uuid = windows_uuid_from_registry if uuid.nil?
103
- raise Train::TransportError, "Cannot find a UUID for your node." if uuid.nil?
104
-
105
- uuid
106
- end
107
-
108
- def windows_uuid_from_machine_file
109
- %W{
110
- #{ENV["SYSTEMDRIVE"]}\\chef\\chef_guid
111
- #{ENV["HOMEDRIVE"]}#{ENV["HOMEPATH"]}\\.chef\\chef_guid
112
- }.each do |path|
113
- file = @backend.file(path)
114
- return file.content.chomp if file.exist? && file.size != 0
115
- end
116
- nil
117
- end
118
-
119
- def windows_uuid_from_chef
120
- file = @backend.file("#{ENV["SYSTEMDRIVE"]}\\chef\\cache\\data_collector_metadata.json")
121
- return if !file.exist? || file.size == 0
122
-
123
- json = JSON.parse(file.content)
124
- json["node_uuid"] if json["node_uuid"]
125
- end
126
-
127
- def windows_uuid_from_wmic
128
- result = @backend.run_command("wmic csproduct get UUID")
129
- return unless result.exit_status == 0
130
-
131
- result.stdout.split("\r\n")[-1].strip
132
- end
133
-
134
- def windows_uuid_from_registry
135
- cmd = '(Get-ItemProperty "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography" -Name "MachineGuid")."MachineGuid"'
136
- result = @backend.run_command(cmd)
137
- return unless result.exit_status == 0
138
-
139
- result.stdout.chomp
140
- end
141
- end
142
- end