Module: Train::Platforms::Detect::Helpers::OSCommon

Includes:
Linux, Windows
Included in:
Scanner, UUID
Defined in:
lib/train/platforms/detect/helpers/os_common.rb

Instance Method Summary collapse

Methods included from Windows

#check_cmd, #check_powershell, #detect_windows, #local_windows?, #read_cim_cpu, #read_cim_os, #read_wmic, #read_wmic_cpu, #windows_uuid, #windows_uuid_from_chef, #windows_uuid_from_cim, #windows_uuid_from_machine_file, #windows_uuid_from_registry, #windows_uuid_from_wmic, #windows_uuid_from_wmic_or_cim, #wmic_available?

Methods included from Linux

#linux_os_release, #lsb_config, #lsb_release, #parse_os_release_info, #read_linux_lsb, #redhatish, #redhatish_platform, #redhatish_version

Instance Method Details

#backend_nameObject



18
19
20
# File 'lib/train/platforms/detect/helpers/os_common.rb', line 18

def backend_name
  @backend.class.name
end

#brocade_versionObject



76
77
78
79
80
81
82
83
84
# File 'lib/train/platforms/detect/helpers/os_common.rb', line 76

def brocade_version
  return @cache[:brocade] if @cache.key?(:brocade)

  res = command_output("version")

  m = res.match(/^Fabric OS:\s+v(\S+)$/)

  @cache[:brocade] = m && { version: m[1], type: "fos" }
end

#cisco_show_versionObject



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/train/platforms/detect/helpers/os_common.rb', line 86

def cisco_show_version
  return @cache[:cisco] if @cache.key?(:cisco)

  res = command_output("show version")

  m = res.match(/Cisco IOS Software, [^,]+? \(([^,]+?)\), Version (\d+\.\d+)/)
  unless m.nil?
    return @cache[:cisco] = { version: m[2], model: m[1], type: "ios" }
  end

  m = res.match(/Cisco IOS Software, IOS-XE Software, [^,]+? \(([^,]+?)\), Version (\d+\.\d+\.\d+[A-Z]*)/)
  unless m.nil?
    return @cache[:cisco] = { version: m[2], model: m[1], type: "ios-xe" }
  end

  # CSR 1000V (for example) does not specify model
  m = res.match(/Cisco IOS XE Software, Version (\d+\.\d+\.\d+[A-Z]*)/)
  unless m.nil?
    return @cache[:cisco] = { version: m[1], type: "ios-xe" }
  end

  m = res.match(/Cisco Nexus Operating System \(NX-OS\) Software/)
  unless m.nil?
    v = res[/^\s*system:\s+version (\d+\.\d+)/, 1]
    v ||= res[/NXOS: version (\d+\.\d+)/, 1]
    return @cache[:cisco] = { version: v, type: "nexus" }
  end

  @cache[:cisco] = nil
end

#command_output(cmd) ⇒ Object



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/train/platforms/detect/helpers/os_common.rb', line 36

def command_output(cmd)
  res = @backend.run_command(cmd)
  # To suppress warning: literal string will be frozen in the future
  stdout = String.new(res.stdout)
  stderr = String.new(res.stderr)
  # When you try to execute command using ssh connection as root user and you have provided ssh user identity file
  # it gives standard output to login as authorized user other than root. To show this standard output as an error
  # to user we are matching the string of stdout and raising the error here so that user gets exact information.
  if @backend.class.to_s == "Train::Transports::SSH::Connection"
    if stdout =~ /Please login as the user/
      raise Train::UserError, "SSH failed: #{stdout}"
    end

    if stderr =~ /WARNING: Your password has expired/
      raise Train::UserError, "SSH failed: #{stderr}"
    end
  end

  stdout.strip! unless stdout.nil?
  stdout
end

#json_cmd(cmd) ⇒ Object



195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/train/platforms/detect/helpers/os_common.rb', line 195

def json_cmd(cmd)
  cmd = @backend.run_command(cmd)
  if cmd.exit_status == 0 && !cmd.stdout.empty?
    require "json" unless defined?(JSON)
    eos_ver = JSON.parse(cmd.stdout)
    @platform[:release] = eos_ver["version"]
    @platform[:arch] = eos_ver["architecture"]
    true
  end
rescue JSON::ParserError
  nil
end

#ruby_host_os(regex) ⇒ Object



10
11
12
# File 'lib/train/platforms/detect/helpers/os_common.rb', line 10

def ruby_host_os(regex)
  regex.match?(::RbConfig::CONFIG["host_os"])
end

#set_from_unameObject



208
209
210
211
212
# File 'lib/train/platforms/detect/helpers/os_common.rb', line 208

def set_from_uname
  @platform[:name]    = unix_uname_s.lines.first.chomp
  @platform[:release] = unix_uname_r.lines.first.chomp
  true
end

#unix_file_contents(path) ⇒ Object



22
23
24
25
26
27
28
29
30
# File 'lib/train/platforms/detect/helpers/os_common.rb', line 22

def unix_file_contents(path)
  # keep a log of files incase multiple checks call the same one
  return @files[path] if @files.key?(path)

  res = @backend.run_command("test -f #{path} && cat #{path}")
  # ignore files that can't be read
  @files[path] = res.exit_status == 0 ? res.stdout : nil
  @files[path]
end

#unix_file_exist?(path) ⇒ Boolean

Returns:

  • (Boolean)


32
33
34
# File 'lib/train/platforms/detect/helpers/os_common.rb', line 32

def unix_file_exist?(path)
  @backend.run_command("test -f #{path}").exit_status == 0
end

#unix_uname_mObject



70
71
72
73
74
# File 'lib/train/platforms/detect/helpers/os_common.rb', line 70

def unix_uname_m
  return @uname[:m] if @uname.key?(:m)

  @uname[:m] = command_output("uname -m")
end

#unix_uname_rObject



64
65
66
67
68
# File 'lib/train/platforms/detect/helpers/os_common.rb', line 64

def unix_uname_r
  return @uname[:r] if @uname.key?(:r)

  @uname[:r] = command_output("uname -r")
end

#unix_uname_sObject



58
59
60
61
62
# File 'lib/train/platforms/detect/helpers/os_common.rb', line 58

def unix_uname_s
  return @uname[:s] if @uname.key?(:s)

  @uname[:s] = command_output("uname -s")
end

#unix_uuidObject



117
118
119
120
121
122
123
# File 'lib/train/platforms/detect/helpers/os_common.rb', line 117

def unix_uuid
  (unix_uuid_from_chef         ||
   unix_uuid_from_machine_file ||
   uuid_from_command           ||
   uuid_from_containerized_system ||
   raise(Train::TransportError, "Cannot find a UUID for your node."))
end

#unix_uuid_from_chefObject



125
126
127
128
129
130
131
# File 'lib/train/platforms/detect/helpers/os_common.rb', line 125

def unix_uuid_from_chef
  file = @backend.file("/var/chef/cache/data_collector_metadata.json")
  if file.exist? && file.size != 0
    json = ::JSON.parse(file.content)
    return json["node_uuid"] if json["node_uuid"]
  end
end

#unix_uuid_from_machine_fileObject



133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/train/platforms/detect/helpers/os_common.rb', line 133

def unix_uuid_from_machine_file
  %W{
    /etc/chef/chef_guid
    #{ENV["HOME"]}/.chef/chef_guid
    /etc/machine-id
    /var/lib/dbus/machine-id
    /var/db/dbus/machine-id
  }.each do |path|
    file = @backend.file(path)
    next unless file.exist? && file.size != 0
    return file.content.chomp if path =~ /guid/

    return uuid_from_string(file.content.chomp)
  end
  nil
end

#uuid_from_commandObject

This takes a command from the platform detect block to run. We expect the command to return a unique identifier which we turn into a UUID.



153
154
155
156
157
158
# File 'lib/train/platforms/detect/helpers/os_common.rb', line 153

def uuid_from_command
  return unless @platform[:uuid_command]

  result = @backend.run_command(@platform[:uuid_command])
  uuid_from_string(result.stdout.chomp) if result.exit_status == 0 && !result.stdout.empty?
end

#uuid_from_containerized_systemObject

This will run if anyone is running Train with local transport inside docker container This is fallback plan, if other ways of getting uuid fails for local transport running inside docker container TODO: This needs to be improved to support other container runtime



163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/train/platforms/detect/helpers/os_common.rb', line 163

def uuid_from_containerized_system
  uuid = nil

  if File.exist?("/proc/self/cgroup")
    cmd = @backend.run_command("head -1 /proc/self/cgroup|cut -d/ -f3")
    unless cmd.stdout.strip.empty?
      uuid = cmd.stdout.strip
    end
  end

  if uuid.nil? && File.exist?("/proc/self/mountinfo")
    cmd = @backend.run_command("cat /proc/self/mountinfo | grep -i /docker/containers/ | head -n 1 | awk '{print $4}' | awk NF=NF FS=/ | awk '{print $3}'")
    unless cmd.stdout.strip.empty?
      uuid = cmd.stdout.strip
    end
  end
  uuid
end

#uuid_from_string(string) ⇒ Object

This hashes the passed string into SHA1. Then it downgrades the 160bit SHA1 to a 128bit then we format it as a valid UUIDv5.



185
186
187
188
189
190
191
192
193
# File 'lib/train/platforms/detect/helpers/os_common.rb', line 185

def uuid_from_string(string)
  hash = Digest::SHA1.new
  hash.update(string)
  ary = hash.digest.unpack("NnnnnN")
  ary[2] = (ary[2] & 0x0FFF) | (5 << 12)
  ary[3] = (ary[3] & 0x3FFF) | 0x8000
  # rubocop:disable Style/FormatString
  "%08x-%04x-%04x-%04x-%04x%08x" % ary
end

#winrm?Boolean

Returns:

  • (Boolean)


14
15
16
# File 'lib/train/platforms/detect/helpers/os_common.rb', line 14

def winrm?
  backend_name == "TrainPlugins::WinRM::Connection"
end