train-core 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c4a9a2b3342fe17d7bcbfe3fcee05215b867798a4c8f01a6924e8ac57846fa9d
4
- data.tar.gz: 0041aaaeeb3405feda9ec52c0cdc04867245e3b9b2259a122ed9db44f4506504
3
+ metadata.gz: ebf0e0aa7d97a1c6b4f63c707bba2daf62b2fa53df072dc0884c727dfff1016d
4
+ data.tar.gz: 345f982e6e7966172ba88817689a73ac021cd692d97f9d4ae13f6b772d987c13
5
5
  SHA512:
6
- metadata.gz: a8d25991efa5582a917b6eb8b89cfc4bb04c54202558043c601fa29be600f384885672cb7371f76cca5709fecb06b0c3fbdd735b6898c4fea96f39f200b987ce
7
- data.tar.gz: c9421752289a00b535b7fdff660e9e1e19ff23e37647741e8e042a5ec31e36c9b6af1eef8445217f05905b0178981000681f1c0b0b028b623b7ef2408371da6b
6
+ metadata.gz: 5884539b913f79cdc135a05f6e4ed47ddd5bd051ad919977804adcc9e36e2ddbd85bda8dec3185be32cd2de1d1027628d755c5188728ee524e567bc914713116
7
+ data.tar.gz: a061b7dc4e62f0722a2720e13e0634e077565b6dbd52adc1c56a002e2c58c395dbc8c6cb44a9bb033cab14a186883332b35d23ed9efd5941bb657481c2c9a006
@@ -7,7 +7,7 @@ require_relative "train/options"
7
7
  require_relative "train/plugins"
8
8
  require_relative "train/errors"
9
9
  require_relative "train/platforms"
10
- require "uri"
10
+ require "addressable/uri"
11
11
 
12
12
  module Train
13
13
  # Create a new transport instance, with the plugin indicated by the
@@ -101,7 +101,7 @@ module Train
101
101
  creds[:path] ||= uri.path
102
102
  creds[:password] ||=
103
103
  if opts[:www_form_encoded_password] && !uri.password.nil?
104
- URI.decode_www_form_component(uri.password)
104
+ Addressable::URI.unencode_component(uri.password)
105
105
  else
106
106
  uri.password
107
107
  end
@@ -121,24 +121,24 @@ module Train
121
121
  # Parse a URI. Supports empty URI's with paths, e.g. `mock://`
122
122
  #
123
123
  # @param string [string] URI string, e.g. `schema://domain.com`
124
- # @return [URI::Generic] parsed URI object
124
+ # @return [Addressable::URI] parsed URI object
125
125
  def self.parse_uri(string)
126
- URI.parse(string)
127
- rescue URI::InvalidURIError => e
126
+ u = Addressable::URI.parse(string)
128
127
  # A use-case we want to catch is parsing empty URIs with a schema
129
128
  # e.g. mock://. To do this, we match it manually and fake the hostname
130
- case string
131
- when %r{^([a-z]+)://$}
132
- string += "dummy"
133
- when /^([a-z]+):$/
134
- string += "//dummy"
135
- else
136
- raise Train::UserError, e
129
+ if u.scheme && (u.host.nil? || u.host.empty?) && u.path.empty?
130
+ case string
131
+ when %r{^([a-z]+)://$}
132
+ string += "dummy"
133
+ when /^([a-z]+):$/
134
+ string += "//dummy"
135
+ end
136
+ u = Addressable::URI.parse(string)
137
+ u.host = nil
137
138
  end
138
-
139
- uri = URI.parse(string)
140
- uri.host = nil
141
- uri
139
+ u
140
+ rescue Addressable::URI::InvalidURIError => e
141
+ raise Train::UserError, e
142
142
  end
143
143
  private_class_method :parse_uri
144
144
 
@@ -19,16 +19,19 @@ module Train::Platforms
19
19
  end
20
20
 
21
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
22
+ @detect = block if block
23
+
24
+ # TODO: detect shouldn't be a setter and getter at the same time
25
+ @detect ||= ->(_) { false }
26
+ end
27
+
28
+ def to_s
29
+ be = backend ? backend.backend_type : "unknown"
30
+ "%s:%s:%s" % [self.class, be, name]
31
+ end
32
+
33
+ def inspect
34
+ to_s
32
35
  end
33
36
  end
34
37
  end
@@ -70,11 +70,8 @@ module Train::Platforms::Detect::Helpers
70
70
  res = command_output("version")
71
71
 
72
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
73
 
77
- @cache[:brocade] = nil
74
+ @cache[:brocade] = m && { version: m[1], type: "fos" }
78
75
  end
79
76
 
80
77
  def cisco_show_version
@@ -156,5 +153,24 @@ module Train::Platforms::Detect::Helpers
156
153
  # rubocop:disable Style/FormatString
157
154
  "%08x-%04x-%04x-%04x-%04x%08x" % ary
158
155
  end
156
+
157
+ def json_cmd(cmd)
158
+ cmd = @backend.run_command(cmd)
159
+ if cmd.exit_status == 0 && !cmd.stdout.empty?
160
+ require "json"
161
+ eos_ver = JSON.parse(cmd.stdout)
162
+ @platform[:release] = eos_ver["version"]
163
+ @platform[:arch] = eos_ver["architecture"]
164
+ true
165
+ end
166
+ rescue JSON::ParserError
167
+ nil
168
+ end
169
+
170
+ def set_from_uname
171
+ @platform[:name] = unix_uname_s.lines.first.chomp
172
+ @platform[:release] = unix_uname_r.lines.first.chomp
173
+ true
174
+ end
159
175
  end
160
176
  end
@@ -19,6 +19,13 @@ module Train::Platforms::Detect::Helpers
19
19
  end
20
20
  end
21
21
 
22
+ def redhatish(path)
23
+ if (raw = unix_file_contents(path))
24
+ @platform[:release] = redhatish_version(raw)
25
+ true
26
+ end
27
+ end
28
+
22
29
  def linux_os_release
23
30
  data = unix_file_contents("/etc/os-release")
24
31
  return if data.nil?
@@ -44,6 +44,11 @@ module Train::Platforms::Detect::Helpers
44
44
  true
45
45
  end
46
46
 
47
+ def local_windows?
48
+ @backend.class.to_s == "Train::Transports::Local::Connection" &&
49
+ ruby_host_os(/mswin|mingw32|windows/)
50
+ end
51
+
47
52
  # reads os name and version from wmic
48
53
  # @see https://msdn.microsoft.com/en-us/library/bb742610.aspx#EEAA
49
54
  # Thanks to Matt Wrock (https://github.com/mwrock) for this hint
@@ -27,7 +27,7 @@ module Train::Platforms::Detect
27
27
  top.each do |_name, plat|
28
28
  # we are doing a instance_eval here to make sure we have the proper
29
29
  # context with all the detect helper methods
30
- next unless instance_eval(&plat.detect) == true
30
+ next unless instance_eval(&plat.detect)
31
31
 
32
32
  # if we have a match start looking at the children
33
33
  plat_result = scan_children(plat)
@@ -43,7 +43,7 @@ module Train::Platforms::Detect
43
43
 
44
44
  def scan_children(parent)
45
45
  parent.children.each do |plat, condition|
46
- next unless instance_eval(&plat.detect) == true
46
+ next unless instance_eval(&plat.detect)
47
47
 
48
48
  if plat.class == Train::Platforms::Platform
49
49
  return plat if condition.empty? || check_condition(condition)
@@ -1,629 +1,519 @@
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
1
+ # rubocop:disable Style/ParenthesesAroundCondition
9
2
 
10
3
  module Train::Platforms::Detect::Specifications
11
4
  class OS
12
5
  def self.load
13
- plat = Train::Platforms
6
+ load_toplevel
7
+ load_windows
8
+ load_unix
9
+ load_other
10
+ end
14
11
 
15
- # master family
12
+ def self.load_toplevel
16
13
  plat.family("os").detect { true }
14
+ end
17
15
 
18
- plat.family("windows").in_family("os")
19
- .detect do
20
- # Can't return from a `proc` thus the `is_windows` shenanigans
21
- is_windows = false
22
- is_windows = true if winrm?
23
-
24
- if @backend.class.to_s == "Train::Transports::Local::Connection"
25
- is_windows = true if ruby_host_os(/mswin|mingw32|windows/)
26
- end
16
+ def self.load_windows
17
+ declare_category("windows", "os") do
18
+ (winrm? ||
19
+ local_windows? ||
20
+ detect_windows) # ssh
21
+ end
27
22
 
28
- # Try to detect windows even for ssh transport
29
- if !is_windows && detect_windows == true
30
- is_windows = true
31
- end
23
+ declare_family("windows", "windows") do
24
+ detect_windows
25
+ end
26
+ end
32
27
 
33
- is_windows
34
- end
35
- # windows platform
36
- plat.name("windows").in_family("windows")
37
- .detect do
38
- true if detect_windows == true
28
+ def self.load_unix
29
+ declare_category("unix", "os") do
30
+ # we want to catch a special case here where cisco commands
31
+ # don't return an exit status and still print to stdout
32
+ uname = unix_uname_s
33
+ unless (uname.empty? ||
34
+ uname.start_with?("Line has invalid autocommand") ||
35
+ uname.start_with?("The command you have entered"))
36
+ @platform[:arch] = unix_uname_m
37
+ true
39
38
  end
39
+ end
40
40
 
41
- # unix master family
42
- plat.family("unix").in_family("os")
43
- .detect do
44
- # we want to catch a special case here where cisco commands
45
- # don't return an exit status and still print to stdout
46
- if unix_uname_s =~ /./ && !unix_uname_s.start_with?("Line has invalid autocommand ") && !unix_uname_s.start_with?("The command you have entered")
47
- @platform[:arch] = unix_uname_m
48
- true
49
- end
50
- end
41
+ load_linux
42
+ load_other_unix
43
+ load_bsd
44
+ end
51
45
 
52
- # linux master family
53
- plat.family("linux").in_family("unix")
54
- .detect do
55
- true if unix_uname_s =~ /linux/i
56
- end
46
+ def self.load_linux
47
+ declare_category("linux", "unix") do
48
+ unix_uname_s =~ /linux/i
49
+ end
57
50
 
58
- # debian family
59
- plat.family("debian").in_family("linux")
60
- .detect do
61
- true unless unix_file_contents("/etc/debian_version").nil?
62
- end
63
- plat.name("ubuntu").title("Ubuntu Linux").in_family("debian")
64
- .detect do
65
- lsb = read_linux_lsb
66
- if lsb && lsb[:id] =~ /ubuntu/i
67
- @platform[:release] = lsb[:release]
68
- true
69
- end
70
- end
71
- plat.name("linuxmint").title("LinuxMint").in_family("debian")
72
- .detect do
73
- lsb = read_linux_lsb
74
- if lsb && lsb[:id] =~ /linuxmint/i
75
- @platform[:release] = lsb[:release]
76
- true
77
- end
78
- end
79
- plat.name("raspbian").title("Raspbian Linux").in_family("debian")
80
- .detect do
81
- if (linux_os_release && linux_os_release["NAME"] =~ /raspbian/i) || \
82
- unix_file_exist?("/usr/bin/raspi-config")
83
- @platform[:release] = unix_file_contents("/etc/debian_version").chomp
84
- true
85
- end
86
- end
87
- plat.name("debian").title("Debian Linux").in_family("debian")
88
- .detect do
89
- lsb = read_linux_lsb
90
- if lsb && lsb[:id] =~ /debian/i
91
- @platform[:release] = lsb[:release]
92
- true
93
- end
51
+ declare_category("debian", "linux") do
52
+ unix_file_exist?("/etc/debian_version")
53
+ end
94
54
 
95
- # if we get this far we have to be some type of debian
96
- @platform[:release] = unix_file_contents("/etc/debian_version").chomp
55
+ declare_lsb("ubuntu", "Ubuntu Linux", "debian", /ubuntu/i)
56
+
57
+ declare_lsb("linuxmint", "LinuxMint", "debian", /linuxmint/i)
58
+
59
+ declare_instance("kali", "Kali Linux", "debian") do
60
+ l_o_r = linux_os_release
61
+ if l_o_r && l_o_r["ID"] == "kali"
62
+ @platform[:release] = l_o_r["VERSION"]
97
63
  true
98
64
  end
65
+ end
66
+
67
+ declare_instance("raspbian", "Raspbian Linux", "debian") do
68
+ rel = linux_os_release
69
+ if (rel && rel["NAME"] =~ /raspbian/i) ||
70
+ unix_file_exist?("/usr/bin/raspi-config")
71
+ @platform[:release] = unix_file_contents("/etc/debian_version").chomp
99
72
 
100
- # fedora family
101
- plat.family("fedora").in_family("linux")
102
- .detect do
103
- true if linux_os_release && linux_os_release["NAME"] =~ /fedora/i
104
- end
105
- plat.name("fedora").title("Fedora").in_family("fedora")
106
- .detect do
107
- @platform[:release] = linux_os_release["VERSION_ID"]
108
73
  true
109
74
  end
75
+ end
76
+
77
+ declare_instance("debian", "Debian Linux", "debian") do
78
+ # if we get this far we have to be some type of debian
79
+ @platform[:release] = unix_file_contents("/etc/debian_version").chomp
80
+
81
+ true
82
+ end
83
+
84
+ declare_category("fedora", "linux") do
85
+ rel = linux_os_release
86
+ rel && rel["NAME"] =~ /fedora/i
87
+ end
88
+
89
+ declare_instance("fedora", "Fedora", "fedora") do
90
+ @platform[:release] = linux_os_release["VERSION_ID"]
91
+ true
92
+ end
110
93
 
111
- # arista_eos family
112
- # this checks for the arista bash shell
113
94
  # must come before redhat as it uses fedora under the hood
114
95
  plat.family("arista_eos").title("Arista EOS Family").in_family("linux")
115
96
  .detect do
116
97
  true
117
98
  end
118
- plat.name("arista_eos_bash").title("Arista EOS Bash Shell").in_family("arista_eos")
119
- .detect do
120
- if unix_file_exist?("/usr/bin/FastCli")
121
- cmd = @backend.run_command('FastCli -p 15 -c "show version | json"')
122
- if cmd.exit_status == 0 && !cmd.stdout.empty?
123
- require "json"
124
- begin
125
- eos_ver = JSON.parse(cmd.stdout)
126
- @platform[:release] = eos_ver["version"]
127
- @platform[:arch] = eos_ver["architecture"]
128
- true
129
- rescue JSON::ParserError
130
- nil
131
- end
132
- end
133
- end
99
+
100
+ declare_instance("arista_eos_bash", "Arista EOS Bash Shell", "arista_eos") do
101
+ # this checks for the arista bash shell
102
+ if unix_file_exist?("/usr/bin/FastCli")
103
+ # TODO: no tests
104
+ json_cmd('FastCli -p 15 -c "show version | json"')
134
105
  end
106
+ end
135
107
 
136
- # redhat family
137
- plat.family("redhat").in_family("linux")
138
- .detect do
139
- # I am not sure this returns true for all redhats in this family
140
- # for now we are going to just try each platform
141
- # return true unless unix_file_contents('/etc/redhat-release').nil?
108
+ declare_category("redhat", "linux") do
109
+ true
110
+ end
142
111
 
112
+ declare_instance("centos", "Centos Linux", "redhat") do
113
+ lsb = read_linux_lsb
114
+ if lsb && lsb[:id] =~ /centos/i
115
+ # TODO: no tests
116
+ @platform[:release] = lsb[:release]
117
+ true
118
+ elsif (rel = linux_os_release) && rel["NAME"] =~ /centos/i
119
+ @platform[:release] = redhatish_version(unix_file_contents("/etc/redhat-release"))
143
120
  true
144
121
  end
145
- plat.name("centos").title("Centos Linux").in_family("redhat")
146
- .detect do
147
- lsb = read_linux_lsb
148
- if lsb && lsb[:id] =~ /centos/i
149
- @platform[:release] = lsb[:release]
150
- true
151
- elsif linux_os_release && linux_os_release["NAME"] =~ /centos/i
152
- @platform[:release] = redhatish_version(unix_file_contents("/etc/redhat-release"))
153
- true
154
- end
155
- end
156
- plat.name("oracle").title("Oracle Linux").in_family("redhat")
157
- .detect do
158
- if !(raw = unix_file_contents("/etc/oracle-release")).nil?
159
- @platform[:release] = redhatish_version(raw)
160
- true
161
- elsif !(raw = unix_file_contents("/etc/enterprise-release")).nil?
162
- @platform[:release] = redhatish_version(raw)
163
- true
164
- end
165
- end
166
- plat.name("scientific").title("Scientific Linux").in_family("redhat")
167
- .detect do
168
- lsb = read_linux_lsb
169
- if lsb && lsb[:id] =~ /scientific/i
170
- @platform[:release] = lsb[:release]
171
- true
172
- end
173
- end
174
- plat.name("xenserver").title("Xenserer Linux").in_family("redhat")
175
- .detect do
176
- lsb = read_linux_lsb
177
- if lsb && lsb[:id] =~ /xenserver/i
178
- @platform[:release] = lsb[:release]
179
- true
180
- end
181
- end
182
- plat.name("parallels-release").title("Parallels Linux").in_family("redhat")
183
- .detect do
184
- unless (raw = unix_file_contents("/etc/parallels-release")).nil?
185
- @platform[:name] = redhatish_platform(raw)
186
- @platform[:release] = raw[/(\d\.\d\.\d)/, 1]
187
- true
188
- end
189
- end
190
- plat.name("wrlinux").title("Wind River Linux").in_family("redhat")
191
- .detect do
192
- if linux_os_release && linux_os_release["ID_LIKE"] =~ /wrlinux/i
193
- @platform[:release] = linux_os_release["VERSION"]
194
- true
195
- end
196
- end
197
- plat.name("amazon").title("Amazon Linux").in_family("redhat")
198
- .detect do
199
- lsb = read_linux_lsb
200
- if lsb && lsb[:id] =~ /amazon/i
201
- @platform[:release] = lsb[:release]
202
- true
203
- elsif (raw = unix_file_contents("/etc/system-release")) =~ /amazon/i
204
- @platform[:name] = redhatish_platform(raw)
205
- @platform[:release] = redhatish_version(raw)
206
- true
207
- end
122
+ end
123
+
124
+ declare_instance("oracle", "Oracle Linux", "redhat") do
125
+ (redhatish("/etc/oracle-release") ||
126
+ redhatish("/etc/enterprise-release"))
127
+ end
128
+
129
+ declare_lsb("scientific", "Scientific Linux", "redhat", /scientific/i)
130
+
131
+ declare_lsb("xenserver", "Xenserer Linux", "redhat", /xenserver/i)
132
+
133
+ declare_instance("parallels-release", "Parallels Linux", "redhat") do
134
+ if (raw = unix_file_contents("/etc/parallels-release"))
135
+ @platform[:name] = redhatish_platform(raw)
136
+ @platform[:release] = raw[/(\d\.\d\.\d)/, 1]
137
+ # TODO: no tests
138
+ true
208
139
  end
209
- plat.name("cloudlinux").title("CloudLinux").in_family("redhat")
210
- .detect do
211
- lsb = read_linux_lsb
212
- if lsb && lsb[:id] =~ /cloudlinux/i
213
- @platform[:release] = lsb[:release]
214
- true
215
- elsif (raw = unix_file_contents("/etc/redhat-release")) =~ /cloudlinux/i
216
- @platform[:name] = redhatish_platform(raw)
217
- @platform[:release] = redhatish_version(raw)
218
- true
219
- end
140
+ end
141
+
142
+ declare_instance("wrlinux", "Wind River Linux", "redhat") do
143
+ rel = linux_os_release
144
+ if rel && rel["ID_LIKE"] =~ /wrlinux/i
145
+ @platform[:release] = rel["VERSION"]
146
+ true
220
147
  end
148
+ end
149
+
150
+ declare_lsb_or_content("amazon", "Amazon Linux", "redhat", "/etc/system-release", /amazon/i)
151
+
152
+ declare_lsb_or_content("cloudlinux", "CloudLinux", "redhat", "/etc/redhat-release", /cloudlinux/i)
153
+
221
154
  # keep redhat at the end as a fallback for anything with a redhat-release
222
- plat.name("redhat").title("Red Hat Linux").in_family("redhat")
223
- .detect do
224
- lsb = read_linux_lsb
225
- if lsb && lsb[:id] =~ /redhat/i
226
- @platform[:release] = lsb[:release]
227
- true
228
- elsif !(raw = unix_file_contents("/etc/redhat-release")).nil?
229
- # must be some type of redhat
230
- @platform[:name] = redhatish_platform(raw)
231
- @platform[:release] = redhatish_version(raw)
232
- true
233
- end
234
- end
155
+ declare_lsb_or_content("redhat", "Red Hat Linux", "redhat", "/etc/redhat-release", /redhat/i, /./)
235
156
 
236
- # suse family
237
- plat.family("suse").in_family("linux")
238
- .detect do
239
- if linux_os_release && linux_os_release["ID_LIKE"] =~ /suse/i
240
- @platform[:release] = linux_os_release["VERSION"]
241
- true
242
- elsif !(suse = unix_file_contents("/etc/SuSE-release")).nil?
243
- # https://rubular.com/r/UKaYWolCYFMfp1
244
- version = suse.scan(/VERSION = (\d+)\nPATCHLEVEL = (\d+)/).flatten.join(".")
245
- # https://rubular.com/r/b5PN3hZDxa5amV
246
- version = suse[/VERSION\s?=\s?"?([\d\.]{2,})"?/, 1] if version == ""
247
- @platform[:release] = version
248
- true
249
- end
250
- end
251
- plat.name("opensuse").title("OpenSUSE Linux").in_family("suse")
252
- .detect do
253
- true if (linux_os_release && linux_os_release["NAME"] =~ /^opensuse/i) ||
254
- unix_file_contents("/etc/SuSE-release") =~ /^opensuse/i
255
- end
256
- plat.name("suse").title("Suse Linux").in_family("suse")
257
- .detect do
258
- true if (linux_os_release && linux_os_release["NAME"] =~ /^sles/i) ||
259
- unix_file_contents("/etc/SuSE-release") =~ /suse/i
157
+ declare_category("suse", "linux") do
158
+ rel = linux_os_release
159
+ if rel && rel["ID_LIKE"] =~ /suse/i
160
+ @platform[:release] = rel["VERSION"]
161
+ true
162
+ elsif (suse = unix_file_contents("/etc/SuSE-release"))
163
+ # https://rubular.com/r/UKaYWolCYFMfp1
164
+ version = suse.scan(/VERSION = (\d+)\nPATCHLEVEL = (\d+)/).flatten.join(".")
165
+ # https://rubular.com/r/b5PN3hZDxa5amV
166
+ version = suse[/VERSION\s?=\s?"?([\d\.]{2,})"?/, 1] if version == ""
167
+ @platform[:release] = version
168
+ true
260
169
  end
170
+ end
261
171
 
262
- # arch
263
- plat.name("arch").title("Arch Linux").in_family("linux")
264
- .detect do
265
- unless unix_file_contents("/etc/arch-release").nil?
266
- # Because this is a rolling release distribution,
267
- # use the kernel release, ex. 4.1.6-1-ARCH
268
- @platform[:release] = unix_uname_r
269
- true
270
- end
271
- end
172
+ declare_os_or_file("opensuse", "OpenSUSE Linux", "suse", "/etc/SuSE-release", /^opensuse/i)
272
173
 
273
- # slackware
274
- plat.name("slackware").title("Slackware Linux").in_family("linux")
275
- .detect do
276
- unless (raw = unix_file_contents("/etc/slackware-version")).nil?
277
- @platform[:release] = raw.scan(/(\d+|\.+)/).join
278
- true
279
- end
280
- end
174
+ declare_os_or_file("suse", "Suse Linux", "suse", "/etc/SuSE-release", /^sles/i, /suse/i)
281
175
 
282
- # gentoo
283
- plat.name("gentoo").title("Gentoo Linux").in_family("linux")
284
- .detect do
285
- unless (raw = unix_file_contents("/etc/gentoo-release")).nil?
286
- @platform[:release] = raw.scan(/(\d+|\.+)/).join
287
- true
288
- end
289
- end
176
+ declare_path_and_uname("arch", "Arch Linux", "linux", "/etc/arch-release")
290
177
 
291
- # exherbo
292
- plat.name("exherbo").title("Exherbo Linux").in_family("linux")
293
- .detect do
294
- unless unix_file_contents("/etc/exherbo-release").nil?
295
- # Because this is a rolling release distribution,
296
- # use the kernel release, ex. 4.1.6
297
- @platform[:release] = unix_uname_r
298
- true
299
- end
300
- end
178
+ declare_file_content("slackware", "Slackware Linux", "linux", "/etc/slackware-version")
301
179
 
302
- # alpine
303
- plat.name("alpine").title("Alpine Linux").in_family("linux")
304
- .detect do
305
- unless (raw = unix_file_contents("/etc/alpine-release")).nil?
306
- @platform[:release] = raw.strip
307
- true
308
- end
309
- end
180
+ declare_file_content("gentoo", "Gentoo Linux", "linux", "/etc/gentoo-release")
310
181
 
311
- # coreos
312
- plat.name("coreos").title("CoreOS Linux").in_family("linux")
313
- .detect do
314
- unless unix_file_contents("/etc/coreos/update.conf").nil?
315
- lsb = read_linux_lsb
316
- @platform[:release] = lsb[:release]
317
- true
318
- end
182
+ declare_path_and_uname("exherbo", "Exherbo Linux", "linux", "/etc/exherbo-release")
183
+
184
+ # TODO: try using declare_path
185
+ declare_instance("alpine", "Alpine Linux", "linux") do
186
+ if (raw = unix_file_contents("/etc/alpine-release"))
187
+ @platform[:release] = raw.strip
188
+ # TODO: no tests
189
+ true
319
190
  end
191
+ end
320
192
 
321
- # yocto family
322
- plat.family("yocto").in_family("linux")
323
- .detect do
324
- # /etc/issue isn't specific to yocto, but it's the only way to detect
325
- # the platform because there are no other identifying files
326
- if unix_file_contents("/etc/issue") &&
327
- (unix_file_contents("/etc/issue").match?("Poky") ||
328
- unix_file_contents("/etc/issue").match?("balenaOS"))
329
- true
330
- end
193
+ declare_instance("coreos", "CoreOS Linux", "linux") do
194
+ if unix_file_exist?("/etc/coreos/update.conf")
195
+ lsb = read_linux_lsb
196
+ @platform[:release] = lsb[:release]
197
+ true
331
198
  end
332
- plat.name("yocto").title("Yocto Project Linux").in_family("yocto")
333
- .detect do
334
- if unix_file_contents("/etc/issue").match?("Poky")
335
- # assuming the Poky version is preferred over the /etc/version build
336
- @platform[:release] = unix_file_contents("/etc/issue").match('\d+(\.\d+)+')[0]
337
- true
338
- end
199
+ end
200
+
201
+ declare_category("yocto", "linux") do
202
+ # /etc/issue isn't specific to yocto, but it's the only way to detect
203
+ # the platform because there are no other identifying files
204
+ issue = unix_file_contents("/etc/issue")
205
+
206
+ issue && issue.match?(/Poky|balenaOS/)
207
+ end
208
+
209
+ declare_instance("yocto", "Yocto Project Linux", "yocto") do
210
+ issue = unix_file_contents("/etc/issue")
211
+ if issue.match?("Poky")
212
+ # assuming the Poky version is preferred over the /etc/version build
213
+ @platform[:release] = issue[/\d+(\.\d+)+/]
214
+ true
339
215
  end
340
- plat.name("balenaos").title("balenaOS Linux").in_family("yocto")
341
- .detect do
342
- # balenaOS does have the /etc/os-release file
343
- if unix_file_contents("/etc/issue").match?("balenaOS") &&
344
- linux_os_release["NAME"] =~ /balenaos/i
345
- @platform[:release] = linux_os_release["META_BALENA_VERSION"]
346
- true
347
- end
216
+ end
217
+
218
+ declare_instance("balenaos", "balenaOS Linux", "yocto") do
219
+ # balenaOS does have the /etc/os-release file
220
+ issue = unix_file_contents("/etc/issue")
221
+ if issue.match?("balenaOS") && linux_os_release["NAME"] =~ /balenaos/i
222
+ @platform[:release] = linux_os_release["META_BALENA_VERSION"]
223
+ true
348
224
  end
225
+ end
349
226
 
350
227
  # brocade family detected here if device responds to 'uname' command,
351
228
  # happens when logging in as root
352
229
  plat.family("brocade").title("Brocade Family").in_family("linux")
353
230
  .detect do
354
- !brocade_version.nil?
231
+ # TODO: no tests
232
+ brocade_version
355
233
  end
356
234
 
357
- # generic linux
358
235
  # this should always be last in the linux family list
359
- plat.name("linux").title("Generic Linux").in_family("linux")
360
- .detect do
361
- true
362
- end
236
+ declare_instance("linux", "Generic Linux", "linux") do
237
+ true
238
+ end
239
+ end
363
240
 
364
- # openvms
365
- plat.name("openvms").title("OpenVMS").in_family("unix")
366
- .detect do
367
- if unix_uname_s =~ /unrecognized command verb/i
368
- cmd = @backend.run_command("show system/noprocess")
369
- unless cmd.exit_status != 0 || cmd.stdout.empty?
370
- @platform[:name] = cmd.stdout.downcase.split(" ")[0]
371
- cmd = @backend.run_command('write sys$output f$getsyi("VERSION")')
372
- @platform[:release] = cmd.stdout.downcase.split("\n")[1][1..-1]
373
- cmd = @backend.run_command('write sys$output f$getsyi("ARCH_NAME")')
374
- @platform[:arch] = cmd.stdout.downcase.split("\n")[1]
375
- true
376
- end
377
- end
378
- end
241
+ def self.load_other_unix
242
+ declare_instance("openvms", "OpenVMS", "unix") do
243
+ if unix_uname_s =~ /unrecognized command verb/i
244
+ # TODO: no tests
245
+ cmd = @backend.run_command("show system/noprocess")
379
246
 
380
- # aix
381
- plat.family("aix").in_family("unix")
382
- .detect do
383
- true if unix_uname_s =~ /aix/i
384
- end
385
- plat.name("aix").title("Aix").in_family("aix")
386
- .detect do
387
- out = @backend.run_command("uname -rvp").stdout
388
- m = out.match(/(\d+)\s+(\d+)\s+(.*)/)
389
- unless m.nil?
390
- @platform[:release] = "#{m[2]}.#{m[1]}"
391
- @platform[:arch] = m[3].to_s
247
+ if cmd.exit_status == 0 && !cmd.stdout.empty?
248
+ # TODO: no tests
249
+ @platform[:name] = cmd.stdout.downcase.split(" ")[0]
250
+ cmd = @backend.run_command('write sys$output f$getsyi("VERSION")')
251
+ @platform[:release] = cmd.stdout.downcase.split("\n")[1][1..-1]
252
+ cmd = @backend.run_command('write sys$output f$getsyi("ARCH_NAME")')
253
+ @platform[:arch] = cmd.stdout.downcase.split("\n")[1]
254
+ true
392
255
  end
393
- true
394
256
  end
257
+ end
395
258
 
396
- # solaris family
397
- plat.family("solaris").in_family("unix")
398
- .detect do
399
- if unix_uname_s =~ /sunos/i
400
- unless (version = /^5\.(?<release>\d+)$/.match(unix_uname_r)).nil?
401
- @platform[:release] = version["release"]
402
- end
259
+ declare_category("aix", "unix") do
260
+ unix_uname_s =~ /aix/i
261
+ end
403
262
 
404
- arch = @backend.run_command("uname -p")
405
- @platform[:arch] = arch.stdout.chomp if arch.exit_status == 0
406
- true
407
- end
408
- end
409
- plat.name("smartos").title("SmartOS").in_family("solaris")
410
- .detect do
411
- rel = unix_file_contents("/etc/release")
412
- if /^.*(SmartOS).*$/ =~ rel
413
- true
414
- end
415
- end
416
- plat.name("omnios").title("Omnios").in_family("solaris")
417
- .detect do
418
- rel = unix_file_contents("/etc/release")
419
- unless (m = /^\s*(OmniOS).*r(\d+).*$/.match(rel)).nil?
420
- @platform[:release] = m[2]
421
- true
422
- end
423
- end
424
- plat.name("openindiana").title("Openindiana").in_family("solaris")
425
- .detect do
426
- rel = unix_file_contents("/etc/release")
427
- unless (m = /^\s*(OpenIndiana).*oi_(\d+).*$/.match(rel)).nil?
428
- @platform[:release] = m[2]
429
- true
430
- end
263
+ declare_instance("aix", "Aix", "aix") do
264
+ out = @backend.run_command("uname -rvp").stdout
265
+ if out =~ /(\d+)\s+(\d+)\s+(.*)/
266
+ # TODO: no tests
267
+ @platform[:release] = "#{$2}.#{$1}"
268
+ @platform[:arch] = "#{$3}"
431
269
  end
432
- plat.name("opensolaris").title("Open Solaris").in_family("solaris")
433
- .detect do
434
- rel = unix_file_contents("/etc/release")
435
- unless (m = /^\s*(OpenSolaris).*snv_(\d+).*$/.match(rel)).nil?
436
- @platform[:release] = m[2]
437
- true
438
- end
439
- end
440
- plat.name("nexentacore").title("Nexentacore").in_family("solaris")
441
- .detect do
442
- rel = unix_file_contents("/etc/release")
443
- if /^\s*(NexentaCore)\s.*$/ =~ rel
444
- true
445
- end
446
- end
447
- plat.name("solaris").title("Solaris").in_family("solaris")
448
- .detect do
449
- rel = unix_file_contents("/etc/release")
450
- if !(m = /Oracle Solaris (\d+)/.match(rel)).nil?
451
- # TODO: should be string!
452
- @platform[:release] = m[1]
453
- true
454
- elsif /^\s*(Solaris)\s.*$/ =~ rel
455
- true
456
- end
270
+ true
271
+ end
457
272
 
458
- # must be some unknown solaris
459
- true
460
- end
273
+ declare_category("solaris", "unix") do
274
+ if unix_uname_s =~ /sunos/i
275
+ # TODO: no tests
461
276
 
462
- # hpux
463
- plat.family("hpux").in_family("unix")
464
- .detect do
465
- true if unix_uname_s =~ /hp-ux/i
466
- end
467
- plat.name("hpux").title("Hpux").in_family("hpux")
468
- .detect do
469
- @platform[:release] = unix_uname_r.lines[0].chomp
470
- true
471
- end
277
+ @platform[:release] = $1 if unix_uname_r =~ /^5\.(\d+)$/
472
278
 
473
- # qnx
474
- plat.family("qnx").in_family("unix")
475
- .detect do
476
- true if unix_uname_s =~ /qnx/i
477
- end
478
- plat.name("qnx").title("QNX").in_family("qnx")
479
- .detect do
480
- @platform[:name] = unix_uname_s.lines[0].chomp.downcase
481
- @platform[:release] = unix_uname_r.lines[0].chomp
482
- @platform[:arch] = unix_uname_m
279
+ arch = @backend.run_command("uname -p")
280
+ @platform[:arch] = arch.stdout.chomp if arch.exit_status == 0
483
281
  true
484
282
  end
283
+ end
485
284
 
486
- # bsd family
487
- plat.family("bsd").in_family("unix")
488
- .detect do
489
- # we need a better way to determine this family
490
- # for now we are going to just try each platform
285
+ # TODO: these regexps are probably needlessly wasteful
286
+ declare_path_regexp("smartos", "SmartOS", "solaris", "/etc/release", /^.*(SmartOS).*$/)
287
+
288
+ declare_path("omnios", "Omnios", "solaris", "/etc/release", /^\s*OmniOS.*r(\d+).*$/)
289
+ declare_path("openindiana", "Openindiana", "solaris", "/etc/release", /^\s*OpenIndiana.*oi_(\d+).*$/)
290
+
291
+ declare_path("opensolaris", "Open Solaris", "solaris", "/etc/release", /^\s*OpenSolaris.*snv_(\d+).*$/)
292
+
293
+ # TODO: these regexps are probably needlessly wasteful
294
+ declare_path_regexp("nexentacore", "Nexentacore", "solaris", "/etc/release", /^\s*(NexentaCore)\s.*$/)
295
+
296
+ declare_instance("solaris", "Solaris", "solaris") do
297
+ rel = unix_file_contents("/etc/release")
298
+ if rel =~ /Oracle Solaris (\d+)/
299
+ @platform[:release] = $1
300
+ # TODO: no tests
491
301
  true
492
- end
493
- plat.family("darwin").in_family("bsd")
494
- .detect do
495
- if unix_uname_s =~ /darwin/i
496
- cmd = unix_file_contents("/usr/bin/sw_vers")
497
- unless cmd.nil?
498
- m = cmd.match(/^ProductVersion:\s+(.+)$/)
499
- @platform[:release] = m.nil? ? nil : m[1]
500
- m = cmd.match(/^BuildVersion:\s+(.+)$/)
501
- @platform[:build] = m.nil? ? nil : m[1]
502
- end
503
- @platform[:release] = unix_uname_r.lines[0].chomp if @platform[:release].nil?
504
- @platform[:arch] = unix_uname_m
505
- true
506
- end
507
- end
508
- plat.name("mac_os_x").title("macOS X").in_family("darwin")
509
- .detect do
510
- cmd = unix_file_contents("/System/Library/CoreServices/SystemVersion.plist")
511
- @platform[:uuid_command] = "system_profiler SPHardwareDataType | awk '/UUID/ { print $3; }'"
512
- true if cmd =~ /Mac OS X/i
513
- end
514
- plat.name("darwin").title("Darwin").in_family("darwin")
515
- .detect do
516
- # must be some other type of darwin
517
- @platform[:name] = unix_uname_s.lines[0].chomp
302
+ elsif rel =~ /^\s*(Solaris)\s.*$/
303
+ # TODO: no tests
518
304
  true
519
305
  end
520
- plat.name("freebsd").title("Freebsd").in_family("bsd")
521
- .detect do
522
- if unix_uname_s =~ /freebsd/i
523
- @platform[:name] = unix_uname_s.lines[0].chomp
524
- @platform[:release] = unix_uname_r.lines[0].chomp
525
- true
526
- end
527
- end
528
- plat.name("openbsd").title("Openbsd").in_family("bsd")
529
- .detect do
530
- if unix_uname_s =~ /openbsd/i
531
- @platform[:name] = unix_uname_s.lines[0].chomp
532
- @platform[:release] = unix_uname_r.lines[0].chomp
533
- true
534
- end
535
- end
536
- plat.name("netbsd").title("Netbsd").in_family("bsd")
537
- .detect do
538
- if unix_uname_s =~ /netbsd/i
539
- @platform[:name] = unix_uname_s.lines[0].chomp
540
- @platform[:release] = unix_uname_r.lines[0].chomp
541
- true
542
- end
306
+
307
+ # TODO: no tests
308
+
309
+ # must be some unknown solaris
310
+ true
311
+ end
312
+
313
+ declare_category("hpux", "unix") do
314
+ unix_uname_s =~ /hp-ux/i
315
+ end
316
+
317
+ declare_instance("hpux", "Hpux", "hpux") do
318
+ @platform[:release] = unix_uname_r.lines[0].chomp
319
+ # TODO: no tests
320
+ true
321
+ end
322
+
323
+ declare_category("qnx", "unix") do
324
+ unix_uname_s =~ /qnx/i
325
+ end
326
+
327
+ declare_instance("qnx", "QNX", "qnx") do
328
+ # TODO: refactor these uname patterns
329
+ @platform[:name] = unix_uname_s.lines[0].chomp.downcase
330
+ @platform[:release] = unix_uname_r.lines[0].chomp
331
+ @platform[:arch] = unix_uname_m
332
+ true
333
+ end
334
+ end
335
+
336
+ def self.load_bsd
337
+ declare_category("bsd", "unix") do
338
+ # we need a better way to determine this family
339
+ # for now we are going to just try each platform
340
+ true
341
+ end
342
+
343
+ declare_category("darwin", "bsd") do
344
+ # rubocop:disable Layout/ExtraSpacing
345
+ # rubocop:disable Layout/SpaceAroundOperators
346
+ if unix_uname_s =~ /darwin/i
347
+ @platform[:release] ||= unix_uname_r.lines[0].chomp
348
+ @platform[:arch] = unix_uname_m
349
+ cmd = @backend.run_command("sw_vers -buildVersion")
350
+ @platform[:build] = cmd.stdout.chomp if cmd.exit_status == 0
351
+ true
543
352
  end
353
+ # rubocop:enable Layout/ExtraSpacing
354
+ # rubocop:enable Layout/SpaceAroundOperators
355
+ end
356
+
357
+ declare_instance("mac_os_x", "macOS X", "darwin") do
358
+ cmd = unix_file_contents("/System/Library/CoreServices/SystemVersion.plist")
359
+ @platform[:uuid_command] = "system_profiler SPHardwareDataType | awk '/UUID/ { print $3; }'"
360
+ cmd =~ /Mac OS X/i
361
+ end
362
+
363
+ declare_instance("darwin", "Darwin", "darwin") do
364
+ # must be some other type of darwin
365
+ @platform[:name] = unix_uname_s.lines[0].chomp
366
+ true
367
+ end
368
+
369
+ declare_bsd("freebsd", "Freebsd", "bsd", /freebsd/i)
370
+ declare_bsd("openbsd", "Openbsd", "bsd", /openbsd/i)
371
+ declare_bsd("netbsd", "Netbsd", "bsd", /netbsd/i)
372
+ end
544
373
 
545
- # arista_eos family
374
+ def self.load_other
546
375
  plat.family("arista_eos").title("Arista EOS Family").in_family("os")
547
376
  .detect do
548
377
  true
549
378
  end
550
- plat.name("arista_eos").title("Arista EOS").in_family("arista_eos")
551
- .detect do
552
- cmd = @backend.run_command("show version | json")
553
- if cmd.exit_status == 0 && !cmd.stdout.empty?
554
- require "json"
555
- begin
556
- eos_ver = JSON.parse(cmd.stdout)
557
- @platform[:release] = eos_ver["version"]
558
- @platform[:arch] = eos_ver["architecture"]
559
- true
560
- rescue JSON::ParserError
561
- nil
562
- end
563
- end
564
- end
565
379
 
566
- # esx
380
+ declare_instance("arista_eos", "Arista EOS", "arista_eos") do
381
+ json_cmd("show version | json")
382
+ end
383
+
567
384
  plat.family("esx").title("ESXi Family").in_family("os")
568
385
  .detect do
569
- true if unix_uname_s =~ /vmkernel/i
386
+ unix_uname_s =~ /vmkernel/i
570
387
  end
388
+
571
389
  plat.name("vmkernel").in_family("esx")
572
390
  .detect do
573
- @platform[:name] = unix_uname_s.lines[0].chomp
574
- @platform[:release] = unix_uname_r.lines[0].chomp
575
- true
391
+ # TODO: no tests
392
+ set_from_uname
576
393
  end
577
394
 
578
- # cisco_ios family
579
395
  plat.family("cisco").title("Cisco Family").in_family("os")
580
396
  .detect do
581
- !cisco_show_version.nil?
397
+ cisco_show_version
582
398
  end
583
- plat.name("cisco_ios").title("Cisco IOS").in_family("cisco")
584
- .detect do
585
- v = cisco_show_version
586
- next unless v[:type] == "ios"
587
399
 
588
- @platform[:release] = v[:version]
589
- @platform[:arch] = nil
590
- true
591
- end
592
- plat.name("cisco_ios_xe").title("Cisco IOS XE").in_family("cisco")
400
+ declare_cisco("cisco_ios", "Cisco IOS", "cisco", :cisco_show_version, "ios")
401
+ declare_cisco("cisco_ios_xe", "Cisco IOS XE", "cisco", :cisco_show_version, "ios-xe")
402
+ declare_cisco("cisco_nexus", "Cisco Nexus", "cisco", :cisco_show_version, "nexus", "show version | include Processor")
403
+
404
+ plat.family("brocade").title("Brocade Family").in_family("os")
593
405
  .detect do
594
- v = cisco_show_version
595
- next unless v[:type] == "ios-xe"
406
+ brocade_version
407
+ end
408
+
409
+ declare_cisco("brocade_fos", "Brocade FOS", "brocade", :brocade_version, "fos")
410
+ end
411
+
412
+ ######################################################################
413
+ # Helpers (keep these sorted)
414
+
415
+ def self.plat
416
+ Train::Platforms
417
+ end
418
+
419
+ def self.declare_category(family, parent, &block)
420
+ plat.family(family).in_family(parent).detect(&block)
421
+ end
422
+
423
+ def self.declare_family(name, title = nil, family, &block)
424
+ thingy = plat.name(name).in_family(family)
425
+ thingy.title(title) if title
426
+ thingy.detect(&block)
427
+ end
428
+
429
+ def self.declare_instance(name, title, family, &block)
430
+ plat.name(name).title(title).in_family(family).detect(&block)
431
+ end
432
+
433
+ def self.declare_bsd(name, title, family, regexp)
434
+ declare_instance(name, title, family) do
435
+ # TODO: no tests
436
+ set_from_uname if unix_uname_s =~ regexp
437
+ end
438
+ end
439
+
440
+ def self.declare_cisco(name, title, family, detect, type, uuid = nil)
441
+ declare_instance(name, title, family) do
442
+ v = send(detect)
443
+
444
+ next unless v[:type] == type
596
445
 
597
- @platform[:release] = v[:version]
598
- @platform[:arch] = nil
446
+ @platform[:release] = v[:version]
447
+ @platform[:arch] = nil
448
+ @platform[:uuid_command] = uuid if uuid
449
+ true
450
+ end
451
+ end
452
+
453
+ def self.declare_file_content(name, title, family, path)
454
+ declare_instance(name, title, family) do
455
+ if (raw = unix_file_contents(path))
456
+ # TODO: no tests
457
+ @platform[:release] = raw.scan(/[\d.]+/).join
599
458
  true
600
459
  end
601
- plat.name("cisco_nexus").title("Cisco Nexus").in_family("cisco")
602
- .detect do
603
- v = cisco_show_version
604
- next unless v[:type] == "nexus"
460
+ end
461
+ end
605
462
 
606
- @platform[:release] = v[:version]
607
- @platform[:arch] = nil
608
- @platform[:uuid_command] = "show version | include Processor"
463
+ def self.declare_lsb(name, title, family, regexp)
464
+ declare_lsb_or_content(name, title, family, nil, regexp)
465
+ end
466
+
467
+ def self.declare_lsb_or_content(name, title, family, path, regexp1, regexp2 = regexp1)
468
+ declare_instance(name, title, family) do
469
+ lsb = read_linux_lsb
470
+ if lsb && lsb[:id] =~ regexp1
471
+ @platform[:release] = lsb[:release]
472
+ true
473
+ elsif path && (raw = unix_file_contents(path)) =~ regexp2
474
+ @platform[:name] = redhatish_platform(raw)
475
+ @platform[:release] = redhatish_version(raw)
609
476
  true
610
477
  end
478
+ end
479
+ end
611
480
 
612
- # brocade family
613
- plat.family("brocade").title("Brocade Family").in_family("os")
614
- .detect do
615
- !brocade_version.nil?
616
- end
481
+ def self.declare_os_or_file(name, title, family, path, regexp1, regexp2 = regexp1)
482
+ declare_instance(name, title, family) do
483
+ rel = linux_os_release
484
+ (rel && rel["NAME"] =~ regexp1) ||
485
+ unix_file_contents(path) =~ regexp2
486
+ end
487
+ end
617
488
 
618
- plat.name("brocade_fos").title("Brocade FOS").in_family("brocade")
619
- .detect do
620
- v = brocade_version
621
- next unless v[:type] == "fos"
489
+ def self.declare_path(name, title, family, path, regexp)
490
+ declare_instance(name, title, family) do
491
+ rel = unix_file_contents(path)
492
+ if rel =~ regexp
493
+ # TODO: no tests
494
+ @platform[:release] = $1
495
+ true
496
+ end
497
+ end
498
+ end
622
499
 
623
- @platform[:release] = v[:version]
624
- @platform[:arch] = nil
500
+ def self.declare_path_and_uname(name, title, family, path)
501
+ declare_instance(name, title, family) do
502
+ if unix_file_exist?(path)
503
+ # Because this is a rolling release distribution,
504
+ # use the kernel release, ex. 4.1.6-1-ARCH
505
+ # TODO: unix_uname_r.lines[0].chomp ? -- no tests for /etc/exherbo-release or /etc/arch-release
506
+ @platform[:release] = unix_uname_r
625
507
  true
626
508
  end
509
+ end
510
+ end
511
+
512
+ def self.declare_path_regexp(name, title, family, path, regexp)
513
+ declare_instance(name, title, family) do
514
+ # TODO: no tests
515
+ regexp =~ unix_file_contents(path)
516
+ end
627
517
  end
628
518
  end
629
519
  end