boris 1.0.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. data/LICENSE.md +9 -0
  2. data/README.md +94 -0
  3. data/boris.gemspec +28 -0
  4. data/doc/Array.html +437 -0
  5. data/doc/Boris.html +230 -0
  6. data/doc/Boris/ConnectionAlreadyActive.html +123 -0
  7. data/doc/Boris/ConnectionFailed.html +127 -0
  8. data/doc/Boris/Connector.html +794 -0
  9. data/doc/Boris/InvalidCredentials.html +131 -0
  10. data/doc/Boris/InvalidOption.html +123 -0
  11. data/doc/Boris/InvalidTargetName.html +123 -0
  12. data/doc/Boris/Lumberjack.html +466 -0
  13. data/doc/Boris/MissingCredentials.html +123 -0
  14. data/doc/Boris/NoActiveConnection.html +123 -0
  15. data/doc/Boris/NoProfileDetected.html +123 -0
  16. data/doc/Boris/Options.html +783 -0
  17. data/doc/Boris/Profiles.html +117 -0
  18. data/doc/Boris/Profiles/Linux.html +1151 -0
  19. data/doc/Boris/Profiles/RedHat.html +875 -0
  20. data/doc/Boris/Profiles/Solaris.html +1230 -0
  21. data/doc/Boris/Profiles/Structure.html +2050 -0
  22. data/doc/Boris/Profiles/UNIX.html +893 -0
  23. data/doc/Boris/Profiles/Windows.html +1846 -0
  24. data/doc/Boris/Profiles/Windows/Windows2003.html +304 -0
  25. data/doc/Boris/Profiles/Windows/Windows2008.html +379 -0
  26. data/doc/Boris/Profiles/Windows/Windows2012.html +304 -0
  27. data/doc/Boris/SNMPConnector.html +512 -0
  28. data/doc/Boris/SSHConnector.html +633 -0
  29. data/doc/Boris/Target.html +2002 -0
  30. data/doc/Boris/WMIConnector.html +1134 -0
  31. data/doc/BorisLogger.html +217 -0
  32. data/doc/Hash.html +195 -0
  33. data/doc/String.html +1246 -0
  34. data/doc/_index.html +420 -0
  35. data/doc/class_list.html +53 -0
  36. data/doc/css/common.css +1 -0
  37. data/doc/css/full_list.css +57 -0
  38. data/doc/css/style.css +328 -0
  39. data/doc/file.README.html +183 -0
  40. data/doc/file_list.html +55 -0
  41. data/doc/frames.html +28 -0
  42. data/doc/index.html +183 -0
  43. data/doc/js/app.js +214 -0
  44. data/doc/js/full_list.js +173 -0
  45. data/doc/js/jquery.js +4 -0
  46. data/doc/method_list.html +1468 -0
  47. data/doc/top-level-namespace.html +126 -0
  48. data/lib/boris.rb +30 -0
  49. data/lib/boris/connectors.rb +47 -0
  50. data/lib/boris/connectors/snmp.rb +56 -0
  51. data/lib/boris/connectors/ssh.rb +110 -0
  52. data/lib/boris/connectors/wmi.rb +186 -0
  53. data/lib/boris/errors.rb +17 -0
  54. data/lib/boris/helpers/array.rb +63 -0
  55. data/lib/boris/helpers/constants.rb +20 -0
  56. data/lib/boris/helpers/hash.rb +8 -0
  57. data/lib/boris/helpers/scrubber.rb +51 -0
  58. data/lib/boris/helpers/string.rb +130 -0
  59. data/lib/boris/lumberjack.rb +47 -0
  60. data/lib/boris/options.rb +86 -0
  61. data/lib/boris/profiles/linux/redhat.rb +77 -0
  62. data/lib/boris/profiles/linux_core.rb +216 -0
  63. data/lib/boris/profiles/unix/solaris.rb +307 -0
  64. data/lib/boris/profiles/unix_core.rb +85 -0
  65. data/lib/boris/profiles/windows/windows2003.rb +15 -0
  66. data/lib/boris/profiles/windows/windows2008.rb +23 -0
  67. data/lib/boris/profiles/windows/windows2012.rb +15 -0
  68. data/lib/boris/profiles/windows_core.rb +530 -0
  69. data/lib/boris/structure.rb +167 -0
  70. data/lib/boris/target.rb +340 -0
  71. data/test/connector_tests/test_snmp.rb +35 -0
  72. data/test/connector_tests/test_ssh.rb +51 -0
  73. data/test/connector_tests/test_wmi.rb +129 -0
  74. data/test/helper_tests/test_array.rb +25 -0
  75. data/test/helper_tests/test_hash.rb +10 -0
  76. data/test/helper_tests/test_string.rb +136 -0
  77. data/test/profile_tests/test_core_skeleton +107 -0
  78. data/test/profile_tests/test_linux_core.rb +331 -0
  79. data/test/profile_tests/test_redhat.rb +134 -0
  80. data/test/profile_tests/test_solaris.rb +523 -0
  81. data/test/profile_tests/test_unix_core.rb +117 -0
  82. data/test/profile_tests/test_windows.rb +536 -0
  83. data/test/setup_tests.rb +14 -0
  84. data/test/test_all.rb +8 -0
  85. data/test/test_options.rb +44 -0
  86. data/test/test_structure.rb +136 -0
  87. data/test/test_target.rb +146 -0
  88. metadata +241 -0
@@ -0,0 +1,17 @@
1
+ module Boris
2
+ class MissingCredentials < Exception; end
3
+
4
+ class ConnectionFailed < StandardError; end
5
+
6
+ class ConnectionAlreadyActive < StandardError; end
7
+
8
+ class NoActiveConnection < StandardError; end
9
+
10
+ class NoProfileDetected < StandardError; end
11
+
12
+ class InvalidOption < StandardError; end
13
+
14
+ class InvalidCredentials < ConnectionFailed; end
15
+
16
+ class InvalidTargetName < StandardError; end
17
+ end
@@ -0,0 +1,63 @@
1
+ class Array
2
+ def strip_string_values_in_array
3
+ self.map! do |val|
4
+ val.strip_string_values_in_array if val.is_a?(Array)
5
+ val.strip_string_values_in_hash if val.is_a?(Hash)
6
+ val.is_a?(String) ? val.strip : val
7
+ end
8
+ end
9
+
10
+ def to_ms_product_key
11
+ valid_chars = 'BCDFGHJKMPQRTVWXY2346789'.scan(/./)
12
+
13
+ product_key = nil
14
+
15
+ raw_product_key = []
16
+
17
+ 52.upto(66) do |idx|
18
+ raw_product_key << self[idx]
19
+ end
20
+
21
+ 24.downto(0) do |a|
22
+ b = 0
23
+
24
+ 14.downto(0) do |c|
25
+ b = b * 256 ^ raw_product_key[c]
26
+ raw_product_key[c] = (b / 24).to_i
27
+ b = b.remainder(24)
28
+ end
29
+
30
+ product_key = "#{valid_chars[b]}#{product_key}"
31
+
32
+ if a.remainder(5) == 0 && a != 0
33
+ product_key = "-#{product_key}"
34
+ end
35
+ end
36
+
37
+ return product_key.upcase
38
+ end
39
+
40
+ def to_nil_hash
41
+ h = Hash.new
42
+ self.each do |item|
43
+ if item.kind_of?(Hash)
44
+ h.merge!(item)
45
+ else
46
+ h[item.to_sym] = nil
47
+ end
48
+ end
49
+ return h
50
+ end
51
+
52
+ def to_wwn
53
+ wwn = []
54
+
55
+ 0.upto(7) do |i|
56
+ hex = self[i].to_s(16)
57
+ hex = "0#{hex}" if self[i] < 16
58
+ wwn << hex
59
+ end
60
+
61
+ return wwn.join
62
+ end
63
+ end
@@ -0,0 +1,20 @@
1
+ module Boris
2
+ VENDOR_ADOBE = 'Adobe Systems, Inc.'
3
+ VENDOR_AMD = 'AMD, Inc.'
4
+ VENDOR_APC = 'APC Corp.'
5
+ VENDOR_BROCADE = 'Brocade Communications Corp.'
6
+ VENDOR_CISCO = 'Cisco Systems, Inc.'
7
+ VENDOR_CITRIX = 'Citrix Systems, Inc.'
8
+ VENDOR_DELL = 'Dell Inc.'
9
+ VENDOR_EMULEX = 'Emulex Corp.'
10
+ VENDOR_F5 = 'F5 Networks, Inc.'
11
+ VENDOR_HP = 'Hewlett Packard, Inc.'
12
+ VENDOR_IBM = 'IBM Corp.'
13
+ VENDOR_INTEL = 'Intel Corp.'
14
+ VENDOR_MICROSOFT = 'Microsoft Corp.'
15
+ VENDOR_ORACLE = 'Oracle Corp.'
16
+ VENDOR_QLOGIC = 'QLogic Corp.'
17
+ VENDOR_REDHAT = 'Red Hat Inc.'
18
+ VENDOR_SUSE = 'SUSE Linux GmbH'
19
+ VENDOR_VMWARE = 'VMware, Inc.'
20
+ end
@@ -0,0 +1,8 @@
1
+ class Hash
2
+ def strip_string_values_in_hash
3
+ self.each_pair do |key, val|
4
+ val.strip_string_values_in_array if val.is_a?(Array)
5
+ self[key] = val.strip if val.is_a?(String)
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,51 @@
1
+ module Boris; module Profiles
2
+ module Structure
3
+ def scrub_data!
4
+ [ @file_systems,
5
+ @hosted_shares,
6
+ @installed_applications,
7
+ @installed_patches,
8
+ @installed_services,
9
+ @local_user_groups,
10
+ @network_interfaces
11
+ ].each {|arr| arr.strip_string_values_in_array}
12
+ debug 'string values from within data arrays cleaned up'
13
+
14
+ [@hardware, @network_id, @operating_system].each {|h| h.strip_string_values_in_hash}
15
+ debug 'string values from within data hashes cleaned up'
16
+
17
+ @installed_applications.each do |app|
18
+ app[:license_key].upcase! unless !app[:license_key]
19
+ app[:name].string_clean
20
+ app[:vendor].format_vendor unless !app[:vendor]
21
+ end
22
+ debug 'installed application data cleaned up'
23
+
24
+ @network_id[:hostname].upcase!
25
+ @network_id[:domain].downcase! unless !@network_id[:domain]
26
+ debug 'network id data cleaned up'
27
+
28
+ @network_interfaces.each do |interface|
29
+ interface[:fabric_name].downcase! unless !interface[:fabric_name]
30
+ interface[:mac_address].upcase! unless !interface[:mac_address]
31
+ interface[:model] = interface[:model].format_model unless !interface[:model]
32
+ interface[:node_wwn].downcase! unless !interface[:node_wwn]
33
+ interface[:port_wwn].downcase! unless !interface[:port_wwn]
34
+ interface[:remote_mac_address].upcase! unless !interface[:remote_mac_address]
35
+ interface[:vendor] = interface[:vendor].format_vendor unless !interface[:vendor]
36
+ end
37
+ debug 'network interface data cleaned up'
38
+
39
+ @hardware[:cpu_vendor] = @hardware[:cpu_vendor].string_clean.format_vendor unless !@hardware[:cpu_vendor]
40
+ @hardware[:model] = @hardware[:model].format_model unless !@hardware[:model]
41
+ @hardware[:serial].upcase! unless !@hardware[:serial]
42
+ @hardware[:vendor] = @hardware[:vendor].string_clean.format_vendor unless !@hardware[:vendor]
43
+ debug 'network interface data cleaned up'
44
+
45
+ @operating_system[:license_key].upcase! unless !@operating_system[:license_key]
46
+ debug 'operating system data cleaned up'
47
+
48
+ debug 'data scrubbing complete'
49
+ end
50
+ end
51
+ end; end
@@ -0,0 +1,130 @@
1
+ class String
2
+ include Boris
3
+
4
+ def after_colon
5
+ generic_after(':')
6
+ end
7
+
8
+ def after_period
9
+ generic_after('\.')
10
+ end
11
+
12
+ def after_pipe
13
+ generic_after('|')
14
+ end
15
+
16
+ def after_slash
17
+ generic_after('\\\\|\/')
18
+ end
19
+
20
+ def before_colon
21
+ generic_before(':')
22
+ end
23
+
24
+ def before_period
25
+ generic_before('\.')
26
+ end
27
+
28
+ def before_pipe
29
+ generic_before('|')
30
+ end
31
+
32
+ def before_slash
33
+ generic_before('\\\\|\/')
34
+ end
35
+
36
+ def between_parenthesis
37
+ x = self.scan(/\((.*)\)/)
38
+ return x.empty? ? nil : x.join
39
+ end
40
+
41
+ def format_model
42
+ return nil if self == ''
43
+
44
+ # delete models containing "server" or beginning with "ibm"
45
+ # also remove configuration numbers appended (typically on IBM
46
+ # products... ex 'System x1000 M3 -[123456]-')
47
+ model = self.gsub(/(^ibm|server)/i, '').split(/-*(\[|\()/)[0]
48
+
49
+ model = if model =~ /^sun.*blade/i
50
+ 'SunBlade ' + model.scan(/\d/).join
51
+ elsif model =~ /^sun.*fire/i
52
+ 'SunFire ' + model.scan(/\d/).join
53
+ elsif model =~ /^T\d{4}/
54
+ 'SPARC Enterprise ' + model
55
+ elsif model =~ /^big-*ip/i
56
+ model.sub(/^big-*ip/i, 'BIG-IP')
57
+ elsif model =~ /^wsc\d{4}-*.*/i
58
+ 'Catalyst ' + (model.scan(/\d/).join + '-' + model.scan(/[a-z]$/i).join).sub(/-$/, '')
59
+ else
60
+ model
61
+ end
62
+
63
+ model.strip
64
+ end
65
+
66
+ def format_serial
67
+ return nil if self =~ /(^$|\(*none\)*)/i
68
+
69
+ self.upcase
70
+ end
71
+
72
+ def string_clean
73
+ # remove registered "(R)" mark
74
+ string = self.gsub(/\(r\)/i, '')
75
+
76
+ string.encode(Encoding.find('ASCII'), :undef=>:replace, :replace=>'').strip
77
+ end
78
+
79
+ def format_vendor
80
+ return nil if self == ''
81
+
82
+ vendor = self
83
+
84
+ vendor = if vendor =~ /^(amd|authenticamd)/i; VENDOR_AMD
85
+ elsif vendor =~ /^brocade/i; VENDOR_BROCADE
86
+ elsif vendor =~ /^citrix/i; VENDOR_CITRIX
87
+ elsif vendor =~ /^dell/i; VENDOR_DELL
88
+ elsif vendor =~ /^emulex/i; VENDOR_EMULEX
89
+ elsif vendor =~ /^(compaq|hp|hewlett packard)/i; VENDOR_HP
90
+ elsif vendor =~ /^ibm/i; VENDOR_IBM
91
+ elsif vendor =~ /^(genuineintel|intel )/i; VENDOR_INTEL
92
+ elsif vendor =~ /^(microsoft)/i; VENDOR_MICROSOFT
93
+ elsif vendor =~ /^(oracle|sun[w]*|sun microsys)/i; VENDOR_ORACLE
94
+ elsif vendor =~ /^qlogic/i; VENDOR_QLOGIC
95
+ elsif vendor =~ /^red\s*hat/i; VENDOR_REDHAT
96
+ elsif vendor =~ /^suse linux/i; VENDOR_SUSE
97
+ elsif vendor =~ /^vmware/i; VENDOR_VMWARE
98
+ else vendor
99
+ end
100
+
101
+ vendor.strip
102
+ end
103
+
104
+ def generic_after(delimiter)
105
+ x = self.scan(/^.*[#{delimiter}](.*)$/)
106
+ return x.empty? ? nil : x.join.strip
107
+ end
108
+
109
+ def generic_before(delimiter)
110
+ x = self.scan(/^(.*?)[#{delimiter}]/)
111
+ return x.empty? ? nil : x.join.strip
112
+ end
113
+
114
+ def hex_to_address
115
+ self.scan(/../).map {|octet| octet.hex}.join('.')
116
+ end
117
+
118
+ def remove_arch
119
+ String.new(self).remove_arch!
120
+ end
121
+
122
+ def remove_arch!
123
+ self.replace(self.gsub(/\s+\(*(32|64)(-|\s)*bit\)*/, ''))
124
+ end
125
+
126
+ def within_quotes
127
+ x = self.scan(/["|'](.*?)["|']/).flatten
128
+ return x.empty? ? nil : x
129
+ end
130
+ end
@@ -0,0 +1,47 @@
1
+ class BorisLogger < Logger
2
+ def initialize(output)
3
+ super(output)
4
+
5
+ self.datetime_format = '%m-%d-%Y %H:%M:%S'
6
+
7
+ self.formatter = proc do |severity, time, progname, msg|
8
+ sprintf("%-6s %-20s %-20s %-s\n", severity, time, progname, msg)
9
+ end
10
+ end
11
+ end
12
+
13
+ module Boris
14
+ module Lumberjack
15
+ attr_accessor :logger
16
+
17
+ def debug(msg)
18
+ logger.add(Logger::DEBUG, append_target_name(msg), facility) if logger && logger.debug?
19
+ end
20
+
21
+ def info(msg)
22
+ logger.add(Logger::INFO, append_target_name(msg), facility) if logger && logger.info?
23
+ end
24
+
25
+ def warn(msg)
26
+ logger.add(Logger::WARN, append_target_name(msg), facility) if logger && logger.warn?
27
+ end
28
+
29
+ def error(msg)
30
+ logger.add(Logger::ERROR, append_target_name(msg), facility) if logger && logger.error?
31
+ end
32
+
33
+ def fatal(msg)
34
+ logger.add(Logger::FATAL, append_target_name(msg), facility) if logger && logger.fatal?
35
+ end
36
+
37
+ private
38
+
39
+ def append_target_name(msg)
40
+ @host ? "#{@host}: #{msg}" : msg
41
+ end
42
+
43
+ def facility
44
+ @facility ||= self.class.name.gsub(/::/, '.').gsub(/([a-z])([A-Z])/, "\\1_\\2").downcase
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,86 @@
1
+ module Boris
2
+ class Options
3
+ attr_accessor :options
4
+
5
+ # Creates our options hash where the user can pass in an optional hash to immediately
6
+ # override the default values.
7
+ #
8
+ # credentials = [{:user=>'joe', :password=>'mypassword', :connection_types=>[:ssh, :wmi]}]
9
+ # ssh_keys = ['/home/joe/private_key']
10
+ # options = Boris::Options.new(:log_level=>:debug, :ssh_options=>{:keys=>ssh_keys}, :credentials=>credentials)
11
+ #
12
+ # @option options [Boolean] :auto_scrub_data should the target's data be scrubbed
13
+ # after running #retrieve_all?
14
+ # @option options [Array] :credentials an array of credentials in the format of
15
+ # +:user+, +:password+, +:connection_types+. Only +:user+ is mandatory.
16
+ # @option options [Symbol] :log_level The level of logging. Options are:
17
+ # +:debug+, +:info+, +:warn+, +:error+, +:fatal (default)+
18
+ # @option options [Array] profiles An array of module names of the profiles we wish
19
+ # to have available for use on this target. {Boris::Profiles::RedHat} and
20
+ # {Profiles::Solaris} are always the defaults, and Windows profiles are included
21
+ # as defaults as well if {Boris} is running on a Windows host (where WMI connections
22
+ # are available)
23
+ # @option options [Hash] snmp_options A hash of options supported by ruby-snmp.
24
+ # @option options [Hash] ssh_options A hash of options supported by net-ssh.
25
+ #
26
+ # @raise ArgumentError when invalid arguments are passed
27
+ def initialize(options={})
28
+ @options = {}
29
+
30
+ # set our defaults
31
+ @options[:auto_scrub_data] ||= true
32
+ @options[:credentials] ||= []
33
+ @options[:log_level] ||= :fatal
34
+ @options[:profiles] ||= [Profiles::RedHat, Profiles::Solaris]
35
+ @options[:profiles].concat([Profiles::Windows::Windows2003, Profiles::Windows::Windows2008, Profiles::Windows::Windows2012]) if PLATFORM == :win32
36
+ @options[:snmp_options] ||= {}
37
+ @options[:ssh_options] ||= {}
38
+
39
+ invalid_options = options.keys - @options.keys
40
+ if invalid_options.any?
41
+ raise ArgumentError, "invalid options specified (#{invalid_options.join(", ")})"
42
+ end
43
+
44
+ # override the defaults with passed in Options
45
+ @options.merge!(options)
46
+ end
47
+
48
+ # Getter method for grabbing a value from the Options.
49
+ # puts options[:log_level] #=> :debug
50
+ #
51
+ # @param key symbol of the key-value pair
52
+ # @return returns the value of specified key from Options
53
+ def [](key)
54
+ @options[key]
55
+ end
56
+
57
+ # Setter method for setting the value in the options hash
58
+ # puts options[:log_level] #=> :debug
59
+ # options[:log_level] = :info
60
+ # puts options[:log_level] #=> :info
61
+ # @raise ArgumentError when invalid options are provided
62
+ def []=(key, val)
63
+ raise ArgumentError, 'invalid option provided' if !@options.has_key?(key)
64
+ @options[key] = val
65
+ end
66
+
67
+ # Provides a simple mechanism for adding credentials to the credentials array of Options.
68
+ #
69
+ # @param cred [Hash] a credential hash. Values include +:user+, +:password+, and
70
+ # +:connection_types+. +:user+ is mandatory, and +:connection_types+ should be an Array.
71
+ # @raise ArgumentError when invalid credentials or connection_types are supplied
72
+ def add_credential(cred)
73
+ raise ArgumentError, 'invalid credential supplied (must be Hash)' if !cred.kind_of?(Hash)
74
+ raise ArgumentError, 'username required' if !cred[:user]
75
+
76
+ cred[:connection_types] ||= VALID_CONNECTION_TYPES
77
+
78
+ invalid_options = cred[:connection_types] - VALID_CONNECTION_TYPES
79
+ if invalid_options.any?
80
+ raise ArgumentError, "invalid connection method specified (#{invalid_options.join(', ')})"
81
+ end
82
+
83
+ @options[:credentials] << cred unless @options[:credentials].include?(cred)
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,77 @@
1
+ require 'boris/profiles/linux_core'
2
+
3
+ module Boris; module Profiles
4
+ module RedHat
5
+ include Linux
6
+
7
+ def self.connection_type
8
+ Linux.connection_type
9
+ end
10
+
11
+ def self.matches_target?(connector)
12
+ release_data = connector.values_at(%q{ls -d /etc/[A-Za-z]*[_-][rv]e[lr]* | grep -v "lsb|system" | cut -d '/' -f3 | cut -d '-' -f1 | cut -d '_' -f1}).join(' ')
13
+ return true if release_data =~ /redhat/i
14
+ end
15
+
16
+ def get_file_systems; super; end
17
+ def get_hardware; super; end
18
+
19
+ def get_hosted_shares
20
+ super
21
+
22
+ # TODO add code for gathering list of hosted shares from redhat
23
+ end
24
+
25
+ def get_installed_applications
26
+ super
27
+
28
+ application_data = @connector.values_at('rpm -qa --queryformat "%{NAME}|%{VERSION}|%{VENDOR}|%{ARCH}|%{INSTALLTIME:date}\n" | sort')
29
+
30
+ application_data.each do |application|
31
+ application = application.split('|')
32
+ h = installed_application_template
33
+
34
+ h[:date_installed] = DateTime.parse(application[4])
35
+ h[:install_location] = nil
36
+ h[:name] = application[0]
37
+ h[:vendor] = application[2]
38
+ h[:version] = application[1]
39
+
40
+ @installed_applications << h
41
+ end
42
+ end
43
+
44
+ def get_installed_patches; super; end
45
+
46
+ def get_installed_services
47
+ super
48
+ service_data = @connector.values_at("/sbin/chkconfig --list | awk {'print $1'}")
49
+
50
+ service_data.each do |service|
51
+ h = installed_service_template
52
+ h[:name] = service
53
+
54
+ @installed_services << h
55
+ end
56
+ end
57
+
58
+ def get_local_user_groups; super; end
59
+ def get_network_id; super; end
60
+ def get_network_interfaces; super; end
61
+
62
+ def get_operating_system
63
+ super
64
+
65
+ os_install_date = @connector.value_at("rpm -qa basesystem --queryformat '%{INSTALLTIME:date}\n'")
66
+ kernel_version = @connector.value_at('uname -r')
67
+ os_data = @connector.values_at('lsb_release -a | egrep -i "description|release"')
68
+
69
+ @operating_system[:date_installed] = DateTime.parse(os_install_date)
70
+ @operating_system[:kernel] = kernel_version
71
+
72
+ os_base_name = os_data.grep(/^description/i)[0].after_colon
73
+ @operating_system[:name] = os_base_name.split(/ linux /i)[0] + ' Linux'
74
+ @operating_system[:version] = os_base_name.scan(/linux (.*) release/i).join + ' ' + os_data.grep(/^release/i)[0].after_colon
75
+ end
76
+ end
77
+ end; end