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_wmic, #read_wmic_cpu, #windows_uuid, #windows_uuid_from_chef, #windows_uuid_from_machine_file, #windows_uuid_from_registry, #windows_uuid_from_wmic

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



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

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



85
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
# File 'lib/train/platforms/detect/helpers/os_common.rb', line 85

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
# File 'lib/train/platforms/detect/helpers/os_common.rb', line 36

def command_output(cmd)
  res = @backend.run_command(cmd)
  stdout = res.stdout
  stderr = 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



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

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)
  ::RbConfig::CONFIG["host_os"] =~ regex
end

#set_from_unameObject



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

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



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

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

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

#unix_uname_rObject



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

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

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

#unix_uname_sObject



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

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

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

#unix_uuidObject



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

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



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

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



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

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.



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

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



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

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.



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

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