boris 1.0.2 → 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -0
- data/README.md +58 -30
- data/Rakefile +7 -1
- data/code_lookups.yml +94 -0
- data/lib/boris.rb +17 -2
- data/lib/boris/connectors.rb +3 -2
- data/lib/boris/connectors/ssh.rb +92 -48
- data/lib/boris/connectors/wmi.rb +8 -5
- data/lib/boris/{helpers → core_ext}/array.rb +0 -0
- data/lib/boris/core_ext/datetime.rb +38 -0
- data/lib/boris/{helpers → core_ext}/hash.rb +0 -0
- data/lib/boris/{helpers → core_ext}/string.rb +91 -33
- data/lib/boris/{helpers → core_ext}/time.rb +0 -0
- data/lib/boris/helpers/constants.rb +6 -3
- data/lib/boris/helpers/scrubber.rb +2 -1
- data/lib/boris/options.rb +29 -7
- data/lib/boris/profiler_core.rb +15 -0
- data/lib/boris/profilers/big_ip/big_ip10.rb +10 -0
- data/lib/boris/profilers/big_ip/big_ip11.rb +10 -0
- data/lib/boris/profilers/big_ip_core.rb +210 -0
- data/lib/boris/profilers/brocade_fos/fos6.rb +10 -0
- data/lib/boris/profilers/brocade_fos_core.rb +144 -0
- data/lib/boris/profilers/cisco/ios12.rb +21 -0
- data/lib/boris/profilers/cisco/nxos5.rb +11 -0
- data/lib/boris/profilers/cisco_ios_core.rb +138 -0
- data/lib/boris/profilers/cisco_nxos_core.rb +148 -0
- data/lib/boris/profilers/linux/redhat/rhel5.rb +13 -0
- data/lib/boris/profilers/linux/redhat/rhel6.rb +13 -0
- data/lib/boris/profilers/linux/{redhat.rb → redhat_core.rb} +2 -8
- data/lib/boris/profilers/linux_core.rb +25 -7
- data/lib/boris/profilers/onboard_administrator/oa3.rb +10 -0
- data/lib/boris/profilers/onboard_administrator_core.rb +96 -0
- data/lib/boris/profilers/unix/solaris/solaris10.rb +11 -0
- data/lib/boris/profilers/unix/solaris/solaris11.rb +11 -0
- data/lib/boris/profilers/unix/{solaris.rb → solaris_core.rb} +13 -13
- data/lib/boris/profilers/unix_core.rb +23 -6
- data/lib/boris/profilers/windows/windows2003.rb +1 -2
- data/lib/boris/profilers/windows/windows2008.rb +1 -2
- data/lib/boris/profilers/windows/windows2012.rb +1 -2
- data/lib/boris/profilers/windows_core.rb +29 -8
- data/lib/boris/structure.rb +15 -0
- data/lib/boris/target.rb +19 -3
- data/lib/boris/version.rb +1 -1
- metadata +27 -11
- data/lib/boris/helpers.rb +0 -7
- data/lib/boris/profiler.rb +0 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a3297aef4c56baeca7a8738865341fceca24d74d
|
4
|
+
data.tar.gz: 6212a16c20af52d48a423a12d674821148a79875
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bb9828202fc372a50c514a1921b41e59f952ba5484a266a9d34b06973ee23bfe9228c1bc5e71474c3be2b13669358e1ee46bba4a1d95513cbd6c88bad7c280a9
|
7
|
+
data.tar.gz: 750717213a0603563f5fda2df41b1eaa87cecc9d8e02361a799c3b977573b243ca1668d909977654c11ba772245de695415591293a10b5fb79209d42a19727b9
|
data/CHANGELOG.md
CHANGED
@@ -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
|
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
|
-
*
|
16
|
-
*
|
17
|
-
*
|
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
|
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/
|
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/
|
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
|
-
#
|
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
|
-
#
|
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
|
-
|
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
|
-
|
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
|
-
|
199
|
-
|
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
|
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
|
data/code_lookups.yml
ADDED
@@ -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'
|
data/lib/boris.rb
CHANGED
@@ -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
|
data/lib/boris/connectors.rb
CHANGED
@@ -12,7 +12,7 @@ module Boris
|
|
12
12
|
attr_reader :host
|
13
13
|
attr_reader :options
|
14
14
|
attr_reader :reconnectable
|
15
|
-
attr_reader :
|
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
|
data/lib/boris/connectors/ssh.rb
CHANGED
@@ -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
|
-
@
|
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
|
-
|
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
|
-
|
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
|
-
@
|
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
|
-
@
|
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
|
-
@
|
58
|
-
|
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
|
-
|
61
|
-
warn
|
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
|
-
|
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.
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
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
|
-
|
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
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
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
|
-
|
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
|