train 3.2.14 → 3.2.20

Sign up to get free protection for your applications and to get access to all the features.
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