File: os_common.rb

package info (click to toggle)
ruby-train 3.2.28-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye
  • size: 1,116 kB
  • sloc: ruby: 9,246; sh: 17; makefile: 8
file content (176 lines) | stat: -rw-r--r-- 5,263 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
require_relative "os_linux"
require_relative "os_windows"
require "rbconfig"

module Train::Platforms::Detect::Helpers
  module OSCommon
    include Train::Platforms::Detect::Helpers::Linux
    include Train::Platforms::Detect::Helpers::Windows

    def ruby_host_os(regex)
      ::RbConfig::CONFIG["host_os"] =~ regex
    end

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

    def backend_name
      @backend.class.name
    end

    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

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

    def command_output(cmd)
      res = @backend.run_command(cmd).stdout
      # When you try to execute command using ssh connction as root user and you have provided ssh user identity file
      # it gives standard output to login as authorised user other than root. To show this standard ouput 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" && res =~ /Please login as the user/
        raise Train::UserError, "SSH failed: #{res}"
      end

      res.strip! unless res.nil?
      res
    end

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

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

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

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

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

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

    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

    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

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

      @cache[:cisco] = nil
    end

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

    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

    def unix_uuid_from_machine_file
      # require 'pry';binding.pry
      %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

    # 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.
    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

    # This hashes the passed string into SHA1.
    # Then it downgrades the 160bit SHA1 to a 128bit
    # then we format it as a valid UUIDv5.
    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

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

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