boris 1.0.0.beta.1 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. data/CHANGELOG.md +11 -0
  2. data/README.md +114 -32
  3. data/Rakefile +19 -0
  4. data/lib/boris.rb +0 -13
  5. data/lib/boris/connectors.rb +25 -7
  6. data/lib/boris/connectors/snmp.rb +24 -4
  7. data/lib/boris/connectors/ssh.rb +32 -6
  8. data/lib/boris/connectors/wmi.rb +109 -54
  9. data/lib/boris/errors.rb +1 -1
  10. data/lib/boris/helpers/array.rb +62 -62
  11. data/lib/boris/helpers/constants.rb +22 -19
  12. data/lib/boris/helpers/hash.rb +7 -7
  13. data/lib/boris/helpers/network.rb +68 -0
  14. data/lib/boris/helpers/scrubber.rb +53 -51
  15. data/lib/boris/helpers/string.rb +148 -26
  16. data/lib/boris/lumberjack.rb +74 -47
  17. data/lib/boris/options.rb +28 -16
  18. data/lib/boris/profiler.rb +19 -0
  19. data/lib/boris/profilers/linux/redhat.rb +74 -0
  20. data/lib/boris/{profiles → profilers}/linux_core.rb +13 -4
  21. data/lib/boris/{profiles → profilers}/unix/solaris.rb +25 -15
  22. data/lib/boris/{profiles → profilers}/unix_core.rb +90 -85
  23. data/lib/boris/profilers/windows/windows2003.rb +11 -0
  24. data/lib/boris/profilers/windows/windows2008.rb +26 -0
  25. data/lib/boris/profilers/windows/windows2012.rb +11 -0
  26. data/lib/boris/{profiles → profilers}/windows_core.rb +36 -14
  27. data/lib/boris/structure.rb +173 -167
  28. data/lib/boris/target.rb +110 -168
  29. data/lib/boris/version.rb +3 -0
  30. metadata +32 -115
  31. data/boris.gemspec +0 -28
  32. data/doc/Array.html +0 -437
  33. data/doc/Boris.html +0 -230
  34. data/doc/Boris/ConnectionAlreadyActive.html +0 -123
  35. data/doc/Boris/ConnectionFailed.html +0 -127
  36. data/doc/Boris/Connector.html +0 -794
  37. data/doc/Boris/InvalidCredentials.html +0 -131
  38. data/doc/Boris/InvalidOption.html +0 -123
  39. data/doc/Boris/InvalidTargetName.html +0 -123
  40. data/doc/Boris/Lumberjack.html +0 -466
  41. data/doc/Boris/MissingCredentials.html +0 -123
  42. data/doc/Boris/NoActiveConnection.html +0 -123
  43. data/doc/Boris/NoProfileDetected.html +0 -123
  44. data/doc/Boris/Options.html +0 -783
  45. data/doc/Boris/Profiles.html +0 -117
  46. data/doc/Boris/Profiles/Linux.html +0 -1151
  47. data/doc/Boris/Profiles/RedHat.html +0 -875
  48. data/doc/Boris/Profiles/Solaris.html +0 -1230
  49. data/doc/Boris/Profiles/Structure.html +0 -2050
  50. data/doc/Boris/Profiles/UNIX.html +0 -893
  51. data/doc/Boris/Profiles/Windows.html +0 -1846
  52. data/doc/Boris/Profiles/Windows/Windows2003.html +0 -304
  53. data/doc/Boris/Profiles/Windows/Windows2008.html +0 -379
  54. data/doc/Boris/Profiles/Windows/Windows2012.html +0 -304
  55. data/doc/Boris/SNMPConnector.html +0 -512
  56. data/doc/Boris/SSHConnector.html +0 -633
  57. data/doc/Boris/Target.html +0 -2002
  58. data/doc/Boris/WMIConnector.html +0 -1134
  59. data/doc/BorisLogger.html +0 -217
  60. data/doc/Hash.html +0 -195
  61. data/doc/String.html +0 -1246
  62. data/doc/_index.html +0 -420
  63. data/doc/class_list.html +0 -53
  64. data/doc/css/common.css +0 -1
  65. data/doc/css/full_list.css +0 -57
  66. data/doc/css/style.css +0 -328
  67. data/doc/file.README.html +0 -183
  68. data/doc/file_list.html +0 -55
  69. data/doc/frames.html +0 -28
  70. data/doc/index.html +0 -183
  71. data/doc/js/app.js +0 -214
  72. data/doc/js/full_list.js +0 -173
  73. data/doc/js/jquery.js +0 -4
  74. data/doc/method_list.html +0 -1468
  75. data/doc/top-level-namespace.html +0 -126
  76. data/lib/boris/profiles/linux/redhat.rb +0 -77
  77. data/lib/boris/profiles/windows/windows2003.rb +0 -15
  78. data/lib/boris/profiles/windows/windows2008.rb +0 -23
  79. data/lib/boris/profiles/windows/windows2012.rb +0 -15
  80. data/test/connector_tests/test_snmp.rb +0 -35
  81. data/test/connector_tests/test_ssh.rb +0 -51
  82. data/test/connector_tests/test_wmi.rb +0 -129
  83. data/test/helper_tests/test_array.rb +0 -25
  84. data/test/helper_tests/test_hash.rb +0 -10
  85. data/test/helper_tests/test_string.rb +0 -136
  86. data/test/profile_tests/test_core_skeleton +0 -107
  87. data/test/profile_tests/test_linux_core.rb +0 -331
  88. data/test/profile_tests/test_redhat.rb +0 -134
  89. data/test/profile_tests/test_solaris.rb +0 -523
  90. data/test/profile_tests/test_unix_core.rb +0 -117
  91. data/test/profile_tests/test_windows.rb +0 -536
  92. data/test/setup_tests.rb +0 -14
  93. data/test/test_all.rb +0 -8
  94. data/test/test_options.rb +0 -44
  95. data/test/test_structure.rb +0 -136
  96. data/test/test_target.rb +0 -146
@@ -8,10 +8,17 @@ module Boris
8
8
  KEY_QUERY_VALUE = 1
9
9
  KEY_ENUMERATE_SUB_KEYS = 8
10
10
 
11
- def initialize(host, cred, options, logger=nil)
12
- super(host, cred, options, logger)
11
+ # Create an instance of WMIConnector by passing in a mandatory hostname or IP address,
12
+ # credential to try, and optional Hash of {Boris::Options options}. Under the hood, this
13
+ # class uses the WIN32OLE library.
14
+ #
15
+ # @param [String] host hostname or IP address
16
+ # @param [Hash] cred credential we wish to use
17
+ def initialize(host, cred)
18
+ super(host, cred)
13
19
  end
14
20
 
21
+ # Disconnect from the host.
15
22
  def disconnect
16
23
  super
17
24
  @wmi = nil
@@ -21,6 +28,9 @@ module Boris
21
28
  debug 'connections closed'
22
29
  end
23
30
 
31
+ # Establish our connection. Three connection types are created: one to the WMI cimv2
32
+ # namespace, one to the WMI root namespace, and one to the registry.
33
+ # @return [WMIConnector] instance of WMIConnector
24
34
  def establish_connection
25
35
  super
26
36
 
@@ -62,55 +72,23 @@ module Boris
62
72
  info 'connection does not seem to be available (so we will not retry)'
63
73
  end unless @transport
64
74
 
65
- return self
66
- end
67
-
68
- def value_at(request, conn=:wmi)
69
- values_at(request, conn, limit=1)[0]
70
- end
71
-
72
- def values_at(request, conn=:wmi, limit=nil)
73
- super(request, limit)
74
-
75
- rows = case conn
76
- when :root_wmi
77
- @root_wmi.ExecQuery(request, nil, 48)
78
- when :wmi
79
- @wmi.ExecQuery(request, nil, 48)
80
- end
81
-
82
- return_data = []
83
-
84
- i = 0
85
-
86
- rows.each do |row|
87
- i += 1
88
-
89
- return_hash = {}
90
-
91
- row.Properties_.each do |property|
92
- if property.Name =~ /^attributes/i && property.Value.kind_of?(WIN32OLE)
93
- row.Attributes.Properties_.each do |property|
94
- return_hash[property.Name.downcase.to_sym] = property.Value
95
- end
96
- else
97
- return_hash[property.Name.downcase.to_sym] = property.Value
98
- end
99
- end
100
-
101
- return_data << return_hash
102
-
103
- break if (limit.nil? && i == limit)
104
- end
105
-
106
- info "#{return_data.size} row(s) returned"
107
-
108
- return return_data
75
+ self
109
76
  end
110
77
 
78
+ # Check if we have access to perform an action on the specified key path. This
79
+ # adds a slight overhead in terms of registry read speed, as internally Boris
80
+ # will check for access to enumerate subkeys for each registry key it wants to
81
+ # read, but this does cut down on the number of access errors on the host.
82
+ #
83
+ # # KEY_ENUMERATE_SUB_KEYS and KEY_QUERY_VALUE are constants specified in Boris.
84
+ # # Check Microsoft docs for other possible values.
85
+ # connector.has_access_for('SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall', KEY_ENUMERATE_SUB_KEYS)
86
+ # #=> true
87
+ #
88
+ # @param [String] key_path the registry key we wish to check access for
89
+ # @param [Integer] permission_to_check the access we wish to test
90
+ # @return [Boolean] true if we have access to perform this action on specified key
111
91
  def has_access_for(key_path, permission_to_check=nil)
112
- debug "checking for registry read access for #{key_path}"
113
-
114
92
  access_params = @registry.Methods_('CheckAccess').inParameters.SpawnInstance_
115
93
 
116
94
  access_params.hDefKey = HKEY_LOCAL_MACHINE
@@ -120,8 +98,17 @@ module Boris
120
98
  @registry.ExecMethod_('CheckAccess', access_params).bGranted
121
99
  end
122
100
 
101
+
102
+ # Returns an array of subkey names found at the specified key path under
103
+ # HKEY_LOCAL_MACHINE.
104
+ #
105
+ # connector.registry_subkeys_at('SOFTWARE\Microsoft')
106
+ # #=> ['SOFTWARE\Microsoft\Office', 'SOFTWARE\Microsoft\Windows'...]
107
+ #
108
+ # @param [String] key_path the registry key we wish to test
109
+ # @return [Array] array of subkeys found
123
110
  def registry_subkeys_at(key_path)
124
- return_data = []
111
+ subkeys = []
125
112
 
126
113
  debug "reading registry subkeys at path #{key_path}"
127
114
 
@@ -131,15 +118,23 @@ module Boris
131
118
  in_params.sSubKeyName = key_path
132
119
 
133
120
  @registry.ExecMethod_('EnumKey', in_params).sNames.each do |key|
134
- return_data << key_path + '\\' + key
121
+ subkeys << key_path + '\\' + key
135
122
  end
136
123
  else
137
124
  info "no access for enumerating keys at (#{key_path})"
138
125
  end
139
126
 
140
- return return_data
127
+ subkeys
141
128
  end
142
129
 
130
+ # Returns an array of values found at the specified key path under
131
+ # HKEY_LOCAL_MACHINE.
132
+ #
133
+ # connector.registry_values_at('SOFTWARE\Microsoft')
134
+ # #=> {:valuename=>value, ...}
135
+ #
136
+ # @param [String] key_path the registry key we wish to test
137
+ # @return [Hash] hash of key/value pairs found at the specified key path
143
138
  def registry_values_at(key_path)
144
139
  values = Hash.new
145
140
 
@@ -157,7 +152,7 @@ module Boris
157
152
  subkey_values ||= []
158
153
 
159
154
  subkey_values.each do |value|
160
- if !value.empty?
155
+ if value.length > 0
161
156
  str_params.sValueName = value
162
157
 
163
158
  begin
@@ -170,7 +165,7 @@ module Boris
170
165
 
171
166
  values[value.downcase.to_sym] = x
172
167
  rescue
173
- if $!.message =~ /#{invalid method}/i
168
+ if $!.message =~ /invalid method/i
174
169
  warn "unreadable registry value (#{key_path}\\#{value})"
175
170
  end
176
171
  end
@@ -180,7 +175,67 @@ module Boris
180
175
  info "no access for enumerating values at (#{key_path})"
181
176
  end
182
177
 
183
- return values
178
+ values
184
179
  end
180
+
181
+ # Return a single value from our request.
182
+ #
183
+ # @param [String] request the command we wish to execute over this connection
184
+ # @param [Symbol] conn the channel we should use for our request
185
+ # Options: +:root_wmi+, +:cimv2+ (default)
186
+ # @return [String] the first row/line returned by the host
187
+ def value_at(request, conn=:cimv2)
188
+ values_at(request, conn, limit=1)[0]
189
+ end
190
+
191
+ # Return multiple values from our request, up to the limit specified (or no
192
+ # limit if no limit parameter is specified.
193
+ #
194
+ # @param [String] request the command we wish to execute over this connection
195
+ # @param [Symbol] conn the channel we should use for our request
196
+ # Options: +:root_wmi+, +:wmi+ (default)
197
+ # @param [Integer] limit the optional maximum number of results we wish to return
198
+ # @return [Array] an array of rows returned by the query
199
+ def values_at(request, conn=:cimv2, limit=nil)
200
+ super(request, limit)
201
+
202
+ rows = case conn
203
+ when :root_wmi
204
+ @root_wmi.ExecQuery(request, nil, 48)
205
+ when :cimv2
206
+ @wmi.ExecQuery(request, nil, 48)
207
+ end
208
+
209
+ return_data = []
210
+
211
+ i = 0
212
+
213
+ rows.each do |row|
214
+ i += 1
215
+
216
+ return_hash = {}
217
+
218
+ row.Properties_.each do |property|
219
+ if property.Name =~ /^attributes/i && property.Value.kind_of?(WIN32OLE)
220
+ row.Attributes.Properties_.each do |property|
221
+ return_hash[property.Name.downcase.to_sym] = property.Value
222
+ end
223
+ else
224
+ return_hash[property.Name.downcase.to_sym] = property.Value
225
+ end
226
+ end
227
+
228
+ return_data << return_hash
229
+
230
+ break if (limit.nil? && i == limit)
231
+ end
232
+
233
+ debug "#{return_data.size} row(s) returned"
234
+
235
+ return return_data
236
+ end
237
+
238
+
239
+
185
240
  end
186
241
  end
@@ -7,7 +7,7 @@ module Boris
7
7
 
8
8
  class NoActiveConnection < StandardError; end
9
9
 
10
- class NoProfileDetected < StandardError; end
10
+ class NoProfilerDetected < StandardError; end
11
11
 
12
12
  class InvalidOption < StandardError; end
13
13
 
@@ -1,63 +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
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
63
  end
@@ -1,20 +1,23 @@
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.'
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
+
21
+ PORT_DEFAULTS = {:ssh=>22, :wmi=>135}
22
+ VALID_CONNECTION_TYPES = [:snmp, :ssh, :wmi]
20
23
  end
@@ -1,8 +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
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
8
  end
@@ -0,0 +1,68 @@
1
+ require 'boris/lumberjack'
2
+
3
+ module Boris
4
+ module Network
5
+ extend Lumberjack
6
+
7
+ # Attempts to suggest a connection method based on whether certain TCP ports
8
+ # on the target are responding (22 for SSH, 135 for WMI by default). Can be
9
+ # used to speed up the process of determining whether we should try to
10
+ # connect to our host using different methods, or bypass certain attempts
11
+ # entirely.
12
+ #
13
+ # Boris::Network.suggested_connection_method('linuxserver01') #=> :ssh
14
+ #
15
+ # @param target name we wish to test against
16
+ # @return [Symbol] returns :wmi, :ssh, or nil
17
+ # @see tcp_port_responding?
18
+ def self.suggested_connection_method(target)
19
+ connection_method = nil
20
+
21
+ PORT_DEFAULTS.each_pair do |key, val|
22
+ break if connection_method
23
+
24
+ debug "detecting if #{key.to_s} is available"
25
+
26
+ if tcp_port_responding?(target, val)
27
+ debug "#{key.to_s} seems to be available"
28
+ connection_method = key
29
+ else
30
+ info 'wmi does not appear to be responding'
31
+ end
32
+ end
33
+
34
+ info 'failed to detect connection method' if connection_method.nil?
35
+ connection_method
36
+ end
37
+
38
+ # Checks if the supplied TCP port is responding on the target. Useful for
39
+ # determining which connection type we should use instead of taking more
40
+ # time connecting to the target using different methods just to check if
41
+ # they succeed or not.
42
+ #
43
+ # Boris::Network.tcp_port_responding?('windowsserver01', 22) #=> false
44
+ # Boris::Network.tcp_port_responding?('windowsserver01', 135) #=> true
45
+ #
46
+ # @param target name we wish to test against
47
+ # @param port the TCP port number we wish to test
48
+ # @return [Boolean] returns true of the supplied port is responding
49
+ def self.tcp_port_responding?(target, port)
50
+ status = false
51
+
52
+ debug "checking if port #{port} is responding"
53
+
54
+ begin
55
+ conn = TCPSocket.new(target, port)
56
+ info "port #{port} is responding"
57
+ conn.close
58
+ debug "connection to port closed"
59
+ status = true
60
+ rescue
61
+ info "port #{port} is not responding"
62
+ status = false
63
+ end
64
+
65
+ status
66
+ end
67
+ end
68
+ end