boris 1.0.2 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +13 -0
  3. data/README.md +58 -30
  4. data/Rakefile +7 -1
  5. data/code_lookups.yml +94 -0
  6. data/lib/boris.rb +17 -2
  7. data/lib/boris/connectors.rb +3 -2
  8. data/lib/boris/connectors/ssh.rb +92 -48
  9. data/lib/boris/connectors/wmi.rb +8 -5
  10. data/lib/boris/{helpers → core_ext}/array.rb +0 -0
  11. data/lib/boris/core_ext/datetime.rb +38 -0
  12. data/lib/boris/{helpers → core_ext}/hash.rb +0 -0
  13. data/lib/boris/{helpers → core_ext}/string.rb +91 -33
  14. data/lib/boris/{helpers → core_ext}/time.rb +0 -0
  15. data/lib/boris/helpers/constants.rb +6 -3
  16. data/lib/boris/helpers/scrubber.rb +2 -1
  17. data/lib/boris/options.rb +29 -7
  18. data/lib/boris/profiler_core.rb +15 -0
  19. data/lib/boris/profilers/big_ip/big_ip10.rb +10 -0
  20. data/lib/boris/profilers/big_ip/big_ip11.rb +10 -0
  21. data/lib/boris/profilers/big_ip_core.rb +210 -0
  22. data/lib/boris/profilers/brocade_fos/fos6.rb +10 -0
  23. data/lib/boris/profilers/brocade_fos_core.rb +144 -0
  24. data/lib/boris/profilers/cisco/ios12.rb +21 -0
  25. data/lib/boris/profilers/cisco/nxos5.rb +11 -0
  26. data/lib/boris/profilers/cisco_ios_core.rb +138 -0
  27. data/lib/boris/profilers/cisco_nxos_core.rb +148 -0
  28. data/lib/boris/profilers/linux/redhat/rhel5.rb +13 -0
  29. data/lib/boris/profilers/linux/redhat/rhel6.rb +13 -0
  30. data/lib/boris/profilers/linux/{redhat.rb → redhat_core.rb} +2 -8
  31. data/lib/boris/profilers/linux_core.rb +25 -7
  32. data/lib/boris/profilers/onboard_administrator/oa3.rb +10 -0
  33. data/lib/boris/profilers/onboard_administrator_core.rb +96 -0
  34. data/lib/boris/profilers/unix/solaris/solaris10.rb +11 -0
  35. data/lib/boris/profilers/unix/solaris/solaris11.rb +11 -0
  36. data/lib/boris/profilers/unix/{solaris.rb → solaris_core.rb} +13 -13
  37. data/lib/boris/profilers/unix_core.rb +23 -6
  38. data/lib/boris/profilers/windows/windows2003.rb +1 -2
  39. data/lib/boris/profilers/windows/windows2008.rb +1 -2
  40. data/lib/boris/profilers/windows/windows2012.rb +1 -2
  41. data/lib/boris/profilers/windows_core.rb +29 -8
  42. data/lib/boris/structure.rb +15 -0
  43. data/lib/boris/target.rb +19 -3
  44. data/lib/boris/version.rb +1 -1
  45. metadata +27 -11
  46. data/lib/boris/helpers.rb +0 -7
  47. data/lib/boris/profiler.rb +0 -18
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5f54a0bce9420195098ba5d362bc8ba592de3b2e
4
- data.tar.gz: 6e798f3e9814cd293b3f9a3ea5bceb606be21ffa
3
+ metadata.gz: a3297aef4c56baeca7a8738865341fceca24d74d
4
+ data.tar.gz: 6212a16c20af52d48a423a12d674821148a79875
5
5
  SHA512:
6
- metadata.gz: 4bbc75ecf7d833216e5dc6d8a1501ff243f489c50905a3c3f45493f49da1c1f27d20dcf9bfb122e2304aaf4d10eb72ab13f1438413dfbb38d6966a07a922758b
7
- data.tar.gz: d06c12bf5cfa6b2be0d17ecfae0957d78305f05b01bed97759edb208ba39f37a8b3fe1f8eba72bb2d2f01e4b000513e1ce65c7b3504491a005d05ad52ad93803
6
+ metadata.gz: bb9828202fc372a50c514a1921b41e59f952ba5484a266a9d34b06973ee23bfe9228c1bc5e71474c3be2b13669358e1ee46bba4a1d95513cbd6c88bad7c280a9
7
+ data.tar.gz: 750717213a0603563f5fda2df41b1eaa87cecc9d8e02361a799c3b977573b243ca1668d909977654c11ba772245de695415591293a10b5fb79209d42a19727b9
@@ -1,3 +1,16 @@
1
+ # 1.0.3
2
+ * Added support for F5 Big-IP traffic manager appliances
3
+ * Added support for Cisco IOS devices
4
+ * Added support for Cisco NX-OS devices
5
+ * Added support for Brocade FOS devices
6
+ * Added support to handle the automatic closing of SSH connections by Cisco appliances
7
+ * Added more error support for SSH connections
8
+ * Added new helper methods to String class
9
+ * Added subclasses for each operating system type
10
+ * Moved core extension classes to own directory
11
+ * Solaris: Fixed bug where interfaces without hardware were causing error
12
+ * Windows: Fixed filesystem utilization numbers
13
+
1
14
  # 1.0.2
2
15
  * Fix for devices asking for password when connecting via SSH
3
16
  * Fixed return value for Target#connect
data/README.md CHANGED
@@ -9,12 +9,13 @@
9
9
  ## Introduction
10
10
  Boris is a library that facilitates the communication between you and various networked devices over SNMP, SSH and WMI, pulling a large amount of configuration items including installed software, network settings, serial numbers, user accounts, disk utilization, and more.
11
11
 
12
- Out of the box, Boris has server support for Red Hat, Solaris,and Windows (with other platforms available with future plugins), with a focus on returning precisely formatted data, no matter which platforms your organization may have deployed. Through the use of profilers, Boris can easily be extended by the developer to include other platforms. Highly suitable for small and large environments alike looking to pull configuration data from various platforms.
12
+ Out of the box, Boris has server support for Red Hat, Solaris, and Windows, as well as support for Cisco IOS and NX-OS (and more!), with a focus on returning precisely formatted data, no matter which platforms your organization may have deployed. Through the use of profilers, Boris can easily be extended by the developer to include other platforms. Highly suitable for small and large environments alike looking to pull configuration data from various platforms connected to their network.
13
13
 
14
14
  ## Features
15
- * Currently, pulls information from Red Hat Linux, Solaris, and Windows servers (support for OS X, F5 BIG-IP, and Cisco IOS devices in the works)
16
- * Utilizes SNMP, SSH, and WMI communication technologies
17
- * Expandable to include other networked devices, such as switches, load balancers, and other operating systems
15
+ * Server support: Red Hat Linux, Solaris, and Windows (support for OS X in the works)
16
+ * Appliance support: Cisco IOS, Cisco NX-OS, F5 BIG-IP LTM, Brocade FOS, and HP Onboard Administrator
17
+ * Utilizes SSH and WMI communication technologies (SNMP is baked in but not currently used)
18
+ * Expandable to include other networked devices, such as switches, load balancers, and other appliances and server operating systems
18
19
 
19
20
  ## Installation
20
21
  gem install boris
@@ -24,7 +25,7 @@ Or if using Bundler, add to your Gemfile
24
25
  gem 'boris'
25
26
 
26
27
  ## Example
27
- Let's pull some information from a RedHat Enterprise Linux server on our network:
28
+ Let's pull some information from a Red Hat Enterprise Linux server on our network:
28
29
 
29
30
  ```ruby
30
31
  require 'boris'
@@ -69,20 +70,6 @@ if target.connected?
69
70
  # installed applications, etc.)
70
71
  target.retrieve_all
71
72
 
72
- # if there is more information we want to collect but is not collected by default, we can specify
73
- # our own commands to run against the target via two methods:
74
-
75
- # #get_values returns an Array (each line is an element of the array)
76
- puts target.connector.values_at('cat /etc/redhat-release')
77
-
78
- # #get_value, which returns a String (the first line returned from the command)
79
- puts target.connector.value_at('uname -a')
80
-
81
- # NOTE: if this were a Windows server, you would send WMI queries instead of shell commands, ie:
82
- #
83
- # target.connector.values_at('SELECT * FROM Win32_ComputerSystem')
84
- #
85
-
86
73
  puts target.to_json(:pretty_print)
87
74
 
88
75
  target.disconnect
@@ -139,6 +126,7 @@ Through a number of queries and algorithms, Boris efficiently polls devices on t
139
126
  * **network ID** - hostname and domain
140
127
  * **network interfaces** - ethernet and fibre channel interfaces, including IPs, MAC addresses, connection status
141
128
  * **operating system** - name, version, kernel, date installed
129
+ * **running processes** - process command, start time and cpu time
142
130
 
143
131
  See [Boris::Profilers::Structure](http://www.rubydoc.info/github/alkalinecoffee/boris/Boris/Profilers/Structure) for more details on the data structure.
144
132
 
@@ -149,16 +137,34 @@ Profilers contain the instructions that allow us to run commands against our tar
149
137
 
150
138
  **Available profilers:**
151
139
 
140
+ * **[Big-IP Core](http://rubydoc.info/github/alkalinecoffee/boris/master/Boris/Profilers/BigIP)**
141
+ * [Big-IP 10](http://rubydoc.info/github/alkalinecoffee/boris/master/Boris/Profilers/BigIP10)
142
+ * [Big-IP 11](http://rubydoc.info/github/alkalinecoffee/boris/master/Boris/Profilers/BigIP11)
143
+ * **[Brocade FOS Core](http://rubydoc.info/github/alkalinecoffee/boris/master/Boris/Profilers/BrocadeFOSCore)**
144
+ * [FOS 6](http://rubydoc.info/github/alkalinecoffee/boris/master/Boris/Profilers/FOS6)
145
+ * **[Cisco IOS Core](http://rubydoc.info/github/alkalinecoffee/boris/master/Boris/Profilers/CiscoIOS)**
146
+ * [Cisco IOS 12](http://rubydoc.info/github/alkalinecoffee/boris/master/Boris/Profilers/IOS12)
147
+ * **[Cisco NX-OS Core](http://rubydoc.info/github/alkalinecoffee/boris/master/Boris/Profilers/CiscoNXOS)**
148
+ * [Cisco NX-OS 5](http://rubydoc.info/github/alkalinecoffee/boris/master/Boris/Profilers/NXOS5)
149
+ * **[HP Onboard Administrator Core](http://rubydoc.info/github/alkalinecoffee/boris/master/Boris/Profilers/OnboardAdministratorCore)**
150
+ * [OA3](http://rubydoc.info/github/alkalinecoffee/boris/master/Boris/Profilers/OA3)
152
151
  * **[Linux Core](http://rubydoc.info/github/alkalinecoffee/boris/master/Boris/Profilers/Linux)**
153
- * [Red Hat Linux](http://rubydoc.info/github/alkalinecoffee/boris/master/Boris/Profilers/RedHat)
152
+ * [Red Hat Enterprise Linux 5](http://rubydoc.info/github/alkalinecoffee/boris/master/Boris/Profilers/RHEL5)
153
+ * [Red Hat Enterprise Linux 6](http://rubydoc.info/github/alkalinecoffee/boris/master/Boris/Profilers/RHEL6)
154
154
  * **[UNIX Core](http://rubydoc.info/github/alkalinecoffee/boris/master/Boris/Profilers/UNIX)**
155
- * [Oracle Solaris](http://rubydoc.info/github/alkalinecoffee/boris/master/Boris/Profilers/Solaris)
155
+ * [Oracle Solaris 10](http://rubydoc.info/github/alkalinecoffee/boris/master/Boris/Profilers/Solaris10)
156
+ * [Oracle Solaris 11](http://rubydoc.info/github/alkalinecoffee/boris/master/Boris/Profilers/Solaris11)
156
157
  * **[Windows Core](http://rubydoc.info/github/alkalinecoffee/boris/master/Boris/Profilers/Windows)**
157
158
  * [Windows 2003 Server](http://rubydoc.info/github/alkalinecoffee/boris/master/Boris/Profilers/Windows2003)
158
159
  * [Windows 2008 Server](http://rubydoc.info/github/alkalinecoffee/boris/master/Boris/Profilers/Windows2008)
159
160
  * [Windows 2012 Server](http://rubydoc.info/github/alkalinecoffee/boris/master/Boris/Profilers/Windows2012)
160
161
 
162
+ Run [Boris#available_profilers](http://www.rubydoc.info/github/alkalinecoffee/boris/Boris.available_profilers).
163
+
161
164
  ## Extending Boris
165
+
166
+ #### Running your own commands
167
+
162
168
  You can also run your own commands to grab information off of systems. For example, on a Linux device, to run your own script that is already on the target and retrieve its output:
163
169
 
164
170
  ```ruby
@@ -182,30 +188,52 @@ multiple_rows_of_data = target.connector.values_at('SELECT * FROM Win32_NetworkA
182
188
  # passing to #values_at):
183
189
  multiple_rows_of_data = target.connector.values_at('SELECT * FROM MSNdis_EnumerateAdapter', :root_wmi)
184
190
 
185
- # you can also poll for registry keys under HKEY_LOCAL_MACHINE by providing a base key path, which returns
186
- # an array of keys:
191
+ # poll registry keys under HKEY_LOCAL_MACHINE by providing a base key path, which returns an array of keys:
187
192
  registry_keys = target.connector.registry_subkeys_at('SOFTWARE\Microsoft\Windows')
188
193
 
189
- # and then grab values found at some key via #registry_values_at, which returns value/data elements in a
190
- # Hash:
194
+ # grab values found at some key via #registry_values_at, which returns value/data elements in a Hash:
191
195
  registry_values = target.connector.registry_values_at('SOFTWARE\Microsoft\Windows\CurrentVersion')
192
196
  ```
193
197
 
194
- **Coming soon--a write-up for SNMP devices**
198
+ #### Creating your own profiler
199
+
200
+ More than likely, you may want to grab information off of a platform that is not supported by Boris. It's easy to create your own profiler by using the profiler skeleton file located in the `skeleton` directory. Simply copy the `profiler_skeleton` file to your app's directory with a `.rb` extension, and modify that file to run the proper commands and retrieve the data from your desired platform, writing the data into the already available instance variables. Once your data retrieval methods are set, simply require your newly created file in your app, and add the class to your `Target#options[:profilers]` array, and it will be available to you.
195
201
 
196
- Boris also comes with the ability to add your own complete modules for using the framework by writing your own data collection algorithms. I will also write-up a howto in the near future.
202
+ Some recommendations on making your own profiler:
203
+ * Create a core file (ex. WindowsCore) for your platform, and only place generalized data-retrieval methods in this file if they would apply to the majority of versions available to that platform
204
+ * Create a new profiler file for each version of your platform (ie. Windows2012), using the core class as its parent class
205
+ * Name your version classes with the major version number applied
206
+ * Only use code that applies to that specific version in your version profiler files
207
+ * Each version file should include a class method called `matches_target?`, where the logic will be to determine if this specific profiler version matches that of the device you're communicating with
208
+ * Stick with the built-in variable names, and use the templates as described in [structure.rb](http://rubydoc.info/github/alkalinecoffee/boris/master/Boris/Structure), and extend them as necessary.
209
+ * Be consistent by always calling `super` on your data-retrieval methods and ending the method by returning the data variable, even if that method does not apply to that platform
210
+ * Check out the available helpers in the `lib/boris/core_ext` directory (especially those in [string.rb](http://rubydoc.info/github/alkalinecoffee/boris/master/String)!)
211
+ * See the profilers in the `lib/boris/profilers` directory for more guidance
197
212
 
198
- ## User Account Requirements
199
- While Boris does its best to gather data from devices without any special privileges, sometimes it just can't be helped. One example of this is the RedHat profiler, which requires `sudo` access for the `dmidecode` command, as there isn't a well known, reliable way to grab this info without `dmidecode`. If Boris attempts to run a command that requires special access and is denied, it will throw a message to the logger and move on.
213
+ Also, please consider a pull request if you think your code can help others!
214
+
215
+ ## System Requirements
216
+ While Boris does its best to gather data from devices without any special privileges, sometimes it just can't be helped. One example of this is the `RedHat` profiler, which requires `sudo` access for the `dmidecode` command, as there isn't a well known, reliable way to grab hardware info without `dmidecode`. If Boris attempts to run a command that requires special access and is denied, it will throw a message to the logger and move on.
200
217
 
201
218
  **Here is a list of known scan account requirements for each platform:**
202
219
 
220
+ * **Big-IP**
221
+ * User shell set to `tmsh`
222
+ * **Brocade FOS**
223
+ * `User` role
224
+ * **Cisco IOS/NX-OS**
225
+ * User EXEC mode (privilege level 1)
226
+ * **HP Onboard Administrator**
227
+ * `User` role
203
228
  * **Linux (any flavor)**
204
229
  * User must have `sudo` for `dmidecode`
205
230
  * **Solaris**
206
231
  * User must have `sudo` for `fcinfo`
207
232
  * **Windows**
208
- * User must be a member of local Administrator group (looking into what other groups provide required access)
233
+ * User must be a member of local Administrator group
234
+
235
+ ## Contributing
236
+ If you have written a profiler (and tests) for a device not currently supported, please create a pull request for it. Also, my testing sucks, so if anyone wants to help clean that up, I'm all about it.
209
237
 
210
238
  ## License
211
239
  This software is provided under the MIT license. See the LICENSE.md file.
data/Rakefile CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'rake'
2
+ require 'rake/testtask'
2
3
 
3
4
  $LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
4
5
 
@@ -16,4 +17,9 @@ task :release => :build do
16
17
  system "git tag -a v#{Boris::VERSION} -m 'Pushed #{Boris::VERSION}'"
17
18
  system 'git push --tags'
18
19
  system "gem push boris-#{Boris::VERSION}.gem"
19
- end
20
+ end
21
+
22
+ Rake::TestTask.new do |t|
23
+ t.libs = ["test"]
24
+ t.test_files = FileList['test/**/test*.rb']
25
+ end
@@ -0,0 +1,94 @@
1
+ brocade:
2
+ blades:
3
+ 1: 'CP1 Control Processor Blade'
4
+ 2: 'Brocade FC-16 Port Blade'
5
+ 4: 'Brocade FC2-16 Port Blade'
6
+ 5: 'CP2 Control Processor Blade'
7
+ 16: 'CP4 Control Processor Blade'
8
+ 17: 'Brocade FC4-16 Port Blade'
9
+ 18: 'Brocade FC4-32 Port Blade'
10
+ 24: 'Brocade FR4-18i SAN Extension Blade'
11
+ 31: 'Brocade FC4-16IP iSCSI Blade'
12
+ 33: 'Brocade FA4-18 Fabric Application Blade'
13
+ 36: 'Brocade FC4-48 Port Blade'
14
+ 37: 'Brocade FC8-16 Port Blade'
15
+ 39: 'Brocade FC10-6 Port Blade'
16
+ 43: 'Brocade FS8-18 Encryption Blade'
17
+ 46: 'CR4S-8 core Blade'
18
+ 50: 'CP8 Control Processor Blade'
19
+ 51: 'Brocade FC8-48 Port Blade'
20
+ 52: 'Core8 switch Blade'
21
+ 55: 'Brocade FC8-32 Port Blade'
22
+ 74: 'Brocade FCOE10-24 Blade'
23
+ 75: 'Brocade FX8-24 Extension Blade'
24
+ 77: 'Brocade FC8-64 Port Blade'
25
+ 96: 'Brocade FC16-48 Port Blade'
26
+ 97: 'Brocade FC16-32 Port Blade'
27
+ 98: 'Brocade CR16-8 Core Blade'
28
+ 99: 'Brocade CR16-16 Core Blade'
29
+ 125: 'Brocade FC8-32E Port Blade'
30
+ 126: 'Brocade FC8-48E Port Blade'
31
+
32
+ switches:
33
+ 1: 'Brocade 1000 Switch'
34
+ 2: 'Brocade 2800 Switch'
35
+ 3: 'Brocade 2100, 2400 Switches'
36
+ 4: 'Brocade 20x0, 2010, 2040, 2050 Switch'
37
+ 5: 'Brocade 22x0, 2210, 2240, 2250 Switch'
38
+ 6: 'Brocade 2800 Switch'
39
+ 7: 'Brocade 2000 Switch'
40
+ 9: 'Brocade 3800 Switch'
41
+ 10: 'Brocade 12000 Director'
42
+ 12: 'Brocade 3900 Switch'
43
+ 16: 'Brocade 3200 Switch'
44
+ 17: 'Brocade 3800VL'
45
+ 18: 'Brocade 3000 Switch'
46
+ 21: 'Brocade 24000 Director'
47
+ 22: 'Brocade 3016 Switch'
48
+ 26: 'Brocade 3850 Switch'
49
+ 27: 'Brocade 3250 Switch'
50
+ 29: 'Brocade 4012 Embedded Switch'
51
+ 32: 'Brocade 4100 Switch'
52
+ 33: 'Brocade 3014 Switch'
53
+ 34: 'Brocade 200E Switch'
54
+ 37: 'Brocade 4020 Embedded Switch'
55
+ 38: 'Brocade 7420 SAN Router'
56
+ 40: 'Fibre Channel Routing (FCR) Front Domain'
57
+ 41: 'Fibre Channel Routing, (FCR) Xlate Domain'
58
+ 42: 'Brocade 48000 Director'
59
+ 43: 'Brocade 4024 Embedded Switch'
60
+ 44: 'Brocade 4900 Switch'
61
+ 45: 'Brocade 4016 Embedded Switch'
62
+ 46: 'Brocade 7500 Switch'
63
+ 51: 'Brocade 4018 Embedded Switch'
64
+ 55: 'Brocade 7600 Switch'
65
+ 58: 'Brocade 5000 Switch'
66
+ 61: 'Brocade 4424 Embedded Switch'
67
+ 62: 'Brocade DCX Backbone'
68
+ 64: 'Brocade 5300 Switch'
69
+ 66: 'Brocade 5100 Switch'
70
+ 67: 'Brocade Encryption Switch'
71
+ 69: 'Brocade 5410 Blade'
72
+ 70: 'Brocade 5410 Embedded Switch'
73
+ 71: 'Brocade 300 Switch'
74
+ 72: 'Brocade 5480 Embedded Switch'
75
+ 73: 'Brocade 5470 Embedded Switch'
76
+ 75: 'Brocade M5424 Embedded Switch'
77
+ 76: 'Brocade 8000 Switch'
78
+ 77: 'Brocade DCX-4S Backbone'
79
+ 83: 'Brocade 7800 Extension Switch'
80
+ 86: 'Brocade 5450 Embedded Switch'
81
+ 87: 'Brocade 5460 Embedded Switch'
82
+ 90: 'Brocade 8470 Embedded Switch'
83
+ 92: 'Brocade VA-40FC Switch'
84
+ 95: 'Brocade VDX 6720-24 Data Center Switch'
85
+ 96: 'Brocade VDX 6730-32 Data Center Switch'
86
+ 97: 'Brocade VDX 6720-60 Data Center Switch'
87
+ 98: 'Brocade VDX 6730-76 Data Center Switch'
88
+ 108: 'Dell M8428-k FCoE Embedded Switch'
89
+ 109: 'Brocade 6510 Switch'
90
+ 116: 'Brocade VDX 6710 Data Center Switch'
91
+ 117: 'Brocade 6547 Embedded Switch'
92
+ 118: 'Brocade 6505 Switch'
93
+ 120: 'Brocade DCX 8510-8 Backbone'
94
+ 121: 'Brocade DCX 8510-4 Backbone'
@@ -1,6 +1,7 @@
1
1
  # encoding: UTF-8
2
2
 
3
3
  PLATFORM = RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/ ? :win32 : RbConfig::CONFIG['host_os']
4
+ LIB_PATH = File.expand_path('..', __FILE__)
4
5
 
5
6
  require 'date'
6
7
  require 'json'
@@ -13,6 +14,20 @@ require 'socket'
13
14
  require 'thread'
14
15
  require 'win32ole' if PLATFORM == :win32
15
16
 
16
- require 'boris/helpers'
17
- require 'boris/lumberjack'
18
17
  require 'boris/target'
18
+
19
+ module Boris
20
+ # Returns an array of all profiler classes that are currently available. If this
21
+ # is launched from a non-Windows machine, the Windows-based profilers will not be
22
+ # available.
23
+ #
24
+ # @return [Array] list of profiler classes
25
+ def self.available_profilers
26
+ ObjectSpace.each_object(Class).select{|klass| klass.to_s =~ /profilers/i}.inject([]) do |result, klass|
27
+ if !(klass.to_s =~ /windows/i && PLATFORM != :win32) && klass.respond_to?(:matches_target?)
28
+ result << klass
29
+ end
30
+ result
31
+ end.sort_by {|klass| klass.to_s}
32
+ end
33
+ end
@@ -12,7 +12,7 @@ module Boris
12
12
  attr_reader :host
13
13
  attr_reader :options
14
14
  attr_reader :reconnectable
15
- attr_reader :failure_message
15
+ attr_reader :failure_messages
16
16
  attr_reader :user
17
17
 
18
18
  def initialize(host, cred={})
@@ -22,6 +22,7 @@ module Boris
22
22
  @user = cred[:user]
23
23
  @password = cred[:password]
24
24
  @connected = false
25
+ @failure_messages = []
25
26
  @reconnectable = true
26
27
  debug 'creating connection object'
27
28
  end
@@ -61,7 +62,7 @@ module Boris
61
62
 
62
63
  amount = limit == 1 ? 'single value' : 'multiple values'
63
64
 
64
- debug "issuing request for #{amount} (#{request})"
65
+ debug "issuing request for #{amount} (#{request.strip})"
65
66
  end
66
67
  end
67
68
  end
@@ -1,3 +1,4 @@
1
+ STDOUT.sync = true
1
2
  require 'boris/connectors'
2
3
 
3
4
  module Boris
@@ -12,26 +13,31 @@ module Boris
12
13
  # @param [Hash] options an optional list of options. See {Boris::Options} for a list of all
13
14
  # possible options. The relevant option set here would be :ssh_options.
14
15
  def initialize(host, cred, options=Options.new)
16
+ super(host, cred)
17
+
15
18
  @ssh_options = options[:ssh_options]
16
-
17
- @ssh_options[:auth_methods] = ['publickey']
18
- if @password
19
- @ssh_options[:auth_methods] << 'password'
20
- @ssh_options[:password] = @password
21
- end
19
+
20
+ @reconnect_mode = false
22
21
 
23
22
  if @ssh_options
24
23
  invalid_ssh_options = @ssh_options.keys - Net::SSH::VALID_OPTIONS
25
24
  raise ArgumentError, "invalid ssh option(s): #{invalid_ssh_options.join(', ')}" if invalid_ssh_options.any?
26
25
  end
27
26
 
28
- super(host, cred)
27
+ @ssh_options[:auth_methods] = ['publickey']
28
+ if @password
29
+ @ssh_options[:auth_methods] << 'password'
30
+ @ssh_options[:password] = @password
31
+ end
29
32
  end
30
33
 
31
34
  # Disconnect from the host.
32
35
  def disconnect
33
36
  super
37
+
38
+ @transport.close rescue nil
34
39
  @transport = nil
40
+
35
41
  debug 'connections closed'
36
42
  end
37
43
 
@@ -40,26 +46,46 @@ module Boris
40
46
  def establish_connection
41
47
  super
42
48
 
49
+ @connected = @reconnectable = false
50
+
43
51
  begin
44
52
  @transport = Net::SSH.start(@host, @user, @ssh_options)
45
- debug 'connection established'
53
+
54
+ # send a newline character to test if the connection is ok. if the return value
55
+ # is nil, then the connection should be good.
56
+ if @reconnect_mode == false
57
+ test_result = value_at("\n")
58
+
59
+ raise PasswordExpired, @failure_messages.last if (test_result && test_result =~ /password has expired/i)
60
+
61
+ debug 'connection established'
62
+ else
63
+ debug 'connection re-established'
64
+ end
65
+
46
66
  @connected = @reconnectable = true
47
67
  rescue Net::SSH::AuthenticationFailed
48
68
  warn "connection failed (connection made but credentials not accepted with user #{@user})"
49
- @failure_message = CONN_FAILURE_AUTH_FAILED
69
+ @failure_messages << CONN_FAILURE_AUTH_FAILED
50
70
  @reconnectable = true
51
71
  rescue Net::SSH::HostKeyMismatch
52
72
  warn CONN_FAILURE_HOST_KEY_MISMATCH
53
- @failure_message = CONN_FAILURE_HOST_KEY_MISMATCH
54
- @reconnectable = false
73
+ @failure_messages << CONN_FAILURE_HOST_KEY_MISMATCH
55
74
  rescue SocketError, Errno::ETIMEDOUT
56
75
  warn CONN_FAILURE_NO_HOST
57
- @failure_message = CONN_FAILURE_NO_HOST
58
- @reconnectable = false
76
+ @failure_messages << CONN_FAILURE_NO_HOST
77
+ rescue Errno::ECONNREFUSED
78
+ warn CONN_FAILURE_REFUSED
79
+ @failure_messages << CONN_FAILURE_REFUSED
80
+ rescue Boris::PasswordExpired
81
+ warn CONN_FAILURE_PASSWORD_EXPIRED
82
+ @failure_messages << CONN_FAILURE_PASSWORD_EXPIRED
83
+ rescue Net::SSH::Disconnect
84
+ warn CONN_FAILURE_CONNECTION_CLOSED
85
+ @failure_messages << CONN_FAILURE_CONNECTION_CLOSED
59
86
  rescue => error
60
- message = "connection failed (#{error.message})"
61
- warn message
62
- @failure_message = message
87
+ @failure_messages << "connection failed (#{error.message})"
88
+ warn @failure_messages.last
63
89
  @reconnectable = true
64
90
  end
65
91
 
@@ -94,59 +120,77 @@ module Boris
94
120
  error_messages = []
95
121
  reconnect = false
96
122
  return_data = []
123
+
124
+ if @reconnect_mode
125
+ disconnect
126
+ establish_connection
127
+ end
97
128
 
98
- chan = @transport.open_channel do |chan|
129
+ channel = @transport.open_channel do |chan, success|
130
+ debug 'channel opened successfully' if success
131
+
99
132
  if request_pty
100
133
  debug 'requsting pty...'
101
134
  chan.request_pty()
102
135
  debug 'pty successfully requested'
103
136
  end
104
137
 
105
- chan.on_data do |ch, data|
106
- if data =~ /^\[sudo\] password for/i
107
- debug 'system asking for password for sudo request'
108
- if @password
109
- ch.send_data "#{@password}\n"
110
- debug 'password sent'
138
+ chan.exec(request) do |chan, success|
139
+ chan.on_data do |chan, data|
140
+ if data =~ /^\[sudo\] password for/i
141
+ debug 'system asking for password for sudo request'
142
+ if @password
143
+ chan.send_data "#{@password}\n"
144
+ debug 'password sent'
145
+ else
146
+ chan.close
147
+ info "channel closed (we don't have a password to supply)"
148
+ end
149
+ elsif data =~ /sorry, try again/i
150
+ chan.close
151
+ return_data = []
152
+ info 'channel closed (we have a password to supply but it is not accepted)'
153
+ elsif data =~ /permission denied/i
154
+ warn "permission denied for this request (#{data.gsub(/\n|\s+/, ', ')})"
111
155
  else
112
- ch.close
113
- info "channel closed (we don't have a password to supply)"
156
+ return_data << data
114
157
  end
115
- elsif data =~ /sorry, try again/i
116
- ch.close
117
- return_data = []
118
- info 'channel closed (we have a password to supply but it is not accepted)'
119
- elsif data =~ /permission denied/i
120
- warn "permission denied for this request (#{data.gsub(/\n|\s+/, ', ')})"
121
- else
122
- return_data << data
123
158
  end
124
- end
125
-
126
- # called when something is written to stderr
127
- chan.on_extended_data do |ch, type, data|
128
- if data =~ /password has expired/i
129
- @failure_message = CONN_FAILURE_PASSWORD_EXPIRED
130
- ch.close
131
- disconnect
159
+
160
+ chan.on_extended_data do |chan, type, data|
161
+ if type == 1
162
+ if data =~ /password has expired/i
163
+ @failure_messages << CONN_FAILURE_PASSWORD_EXPIRED
164
+ chan.close
165
+ disconnect
166
+ end
167
+ error_messages.concat(data.split(/\n/))
168
+ end
132
169
  end
133
- error_messages.concat(data.split(/\n/))
134
170
  end
135
-
136
- chan.exec(request)
137
171
  end
138
172
 
139
- chan.wait
173
+ begin
174
+ channel.wait
175
+ rescue Net::SSH::Disconnect
176
+ # some devices (namely cisco switches) tend to silently break the ssh
177
+ # connection after creating a session and running a command. to get around
178
+ # this, we will re-establish the connection (and bypassing our test command
179
+ # in #establish_connection) to prepare the connection for running another
180
+ # command.
181
+ info 'connection broken by remote host... we will automatically reconnect'
182
+ @reconnect_mode = true
183
+ end
140
184
 
141
185
  if !error_messages.empty?
142
186
  warn "message written to STDERR for this request (#{error_messages.join('. ')})"
143
187
  end
144
188
 
145
- return_data = return_data.join.split(/\n/)
146
-
147
- debug "#{return_data.size} row(s) returned"
189
+ return_data = return_data.join.gsub(/\r/, '').split(/\n/)
148
190
 
149
191
  limit = return_data.size if limit.nil?
192
+
193
+ debug "#{return_data.size} row(s) returned"
150
194
 
151
195
  return_data[0..limit]
152
196
  end