oxidized 0.20.0 → 0.28.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.codeclimate.yml +4 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +12 -0
- data/.github/no-response.yml +13 -0
- data/.github/workflows/publishdocker.yml +13 -0
- data/.gitignore +4 -0
- data/.rubocop.yml +73 -0
- data/.rubocop_todo.yml +120 -0
- data/.travis.yml +6 -1
- data/CHANGELOG.md +693 -243
- data/Dockerfile +27 -19
- data/LICENSE +201 -0
- data/README.md +234 -913
- data/Rakefile +48 -7
- data/TODO.md +29 -23
- data/bin/console +1 -1
- data/bin/oxidized +6 -5
- data/docs/Configuration.md +313 -0
- data/docs/Creating-Models.md +140 -0
- data/docs/Hooks.md +274 -0
- data/docs/Model-Notes/AireOS.md +11 -0
- data/docs/Model-Notes/ArbOS.md +11 -0
- data/docs/Model-Notes/Comware.md +13 -0
- data/docs/Model-Notes/Cumulus.md +40 -0
- data/docs/Model-Notes/EOS.md +12 -0
- data/docs/Model-Notes/IOS.md +29 -0
- data/docs/Model-Notes/JunOS.md +33 -0
- data/docs/Model-Notes/LinuxGeneric.md +24 -0
- data/docs/Model-Notes/Netgear.md +87 -0
- data/docs/Model-Notes/Nokia.md +9 -0
- data/docs/Model-Notes/README.md +24 -0
- data/docs/Model-Notes/SmartAX-Huawei.md +35 -0
- data/docs/Model-Notes/VRP-Huawei.md +34 -0
- data/docs/Model-Notes/Viptela.md +12 -0
- data/docs/Model-Notes/XGS4600-Zyxel.md +36 -0
- data/docs/Outputs.md +190 -0
- data/docs/Ruby-API.md +199 -0
- data/docs/Sources.md +171 -0
- data/docs/Supported-OS-Types.md +227 -0
- data/docs/Troubleshooting.md +66 -0
- data/extra/nagios_check_failing_nodes.rb +9 -2
- data/extra/oxidized-report-git-commits +21 -40
- data/extra/oxidized-ubuntu.haproxy +45 -0
- data/extra/oxidized.logrotate +7 -0
- data/extra/oxidized.service +13 -0
- data/extra/rest_client.rb +7 -10
- data/extra/syslog.rb +47 -42
- data/lib/oxidized/cli.rb +41 -31
- data/lib/oxidized/config/vars.rb +9 -14
- data/lib/oxidized/config.rb +20 -13
- data/lib/oxidized/core.rb +8 -10
- data/lib/oxidized/hook/awssns.rb +6 -7
- data/lib/oxidized/hook/ciscosparkdiff.rb +43 -0
- data/lib/oxidized/hook/exec.rb +19 -24
- data/lib/oxidized/hook/githubrepo.rb +17 -17
- data/lib/oxidized/hook/noophook.rb +1 -1
- data/lib/oxidized/hook/slackdiff.rb +32 -19
- data/lib/oxidized/hook/xmppdiff.rb +59 -0
- data/lib/oxidized/hook.rb +63 -64
- data/lib/oxidized/input/cli.rb +22 -12
- data/lib/oxidized/input/exec.rb +28 -0
- data/lib/oxidized/input/ftp.rb +16 -15
- data/lib/oxidized/input/http.rb +72 -0
- data/lib/oxidized/input/input.rb +6 -6
- data/lib/oxidized/input/ssh.rb +64 -56
- data/lib/oxidized/input/telnet.rb +59 -102
- data/lib/oxidized/input/tftp.rb +9 -10
- data/lib/oxidized/jobs.rb +9 -10
- data/lib/oxidized/manager.rb +42 -44
- data/lib/oxidized/model/acos.rb +19 -20
- data/lib/oxidized/model/acsw.rb +62 -0
- data/lib/oxidized/model/adtran.rb +26 -0
- data/lib/oxidized/model/aen.rb +19 -0
- data/lib/oxidized/model/aireos.rb +9 -10
- data/lib/oxidized/model/airfiber.rb +22 -0
- data/lib/oxidized/model/alteonos.rb +58 -0
- data/lib/oxidized/model/alvarion.rb +0 -4
- data/lib/oxidized/model/aos.rb +11 -5
- data/lib/oxidized/model/aos7.rb +6 -7
- data/lib/oxidized/model/aosw.rb +30 -27
- data/lib/oxidized/model/apc_aos.rb +2 -5
- data/lib/oxidized/model/arbos.rb +26 -0
- data/lib/oxidized/model/aricentiss.rb +49 -0
- data/lib/oxidized/model/asa.rb +61 -22
- data/lib/oxidized/model/asyncos.rb +46 -0
- data/lib/oxidized/model/audiocodes.rb +28 -0
- data/lib/oxidized/model/audiocodesmp.rb +28 -0
- data/lib/oxidized/model/awplus.rb +84 -0
- data/lib/oxidized/model/axos.rb +16 -0
- data/lib/oxidized/model/boss.rb +77 -0
- data/lib/oxidized/model/br6910.rb +42 -45
- data/lib/oxidized/model/c4cmts.rb +6 -10
- data/lib/oxidized/model/cambium.rb +23 -0
- data/lib/oxidized/model/casa.rb +1 -1
- data/lib/oxidized/model/catos.rb +1 -3
- data/lib/oxidized/model/cisconga.rb +1 -3
- data/lib/oxidized/model/ciscosma.rb +42 -0
- data/lib/oxidized/model/ciscosmb.rb +30 -10
- data/lib/oxidized/model/ciscovpn3k.rb +11 -0
- data/lib/oxidized/model/cnos.rb +33 -0
- data/lib/oxidized/model/comnetms.rb +43 -0
- data/lib/oxidized/model/comtrol.rb +41 -0
- data/lib/oxidized/model/comware.rb +28 -16
- data/lib/oxidized/model/coriant8600.rb +3 -5
- data/lib/oxidized/model/coriantgroove.rb +26 -0
- data/lib/oxidized/model/corianttmos.rb +1 -3
- data/lib/oxidized/model/cumulus.rb +60 -49
- data/lib/oxidized/model/datacom.rb +1 -4
- data/lib/oxidized/model/dcnos.rb +46 -0
- data/lib/oxidized/model/dellx.rb +76 -0
- data/lib/oxidized/model/dlink.rb +5 -4
- data/lib/oxidized/model/dnos.rb +11 -5
- data/lib/oxidized/model/eciapollo.rb +34 -0
- data/lib/oxidized/model/edgecos.rb +49 -0
- data/lib/oxidized/model/edgeos.rb +12 -5
- data/lib/oxidized/model/edgeswitch.rb +2 -4
- data/lib/oxidized/model/enterasys.rb +28 -0
- data/lib/oxidized/model/eos.rb +8 -8
- data/lib/oxidized/model/fabricos.rb +4 -6
- data/lib/oxidized/model/fastiron.rb +66 -0
- data/lib/oxidized/model/fiberdriver.rb +2 -2
- data/lib/oxidized/model/firebrick.rb +31 -0
- data/lib/oxidized/model/firelinuxos.rb +41 -0
- data/lib/oxidized/model/firewareos.rb +3 -6
- data/lib/oxidized/model/fortios.rb +31 -19
- data/lib/oxidized/model/ftos.rb +8 -5
- data/lib/oxidized/model/fujitsupy.rb +5 -7
- data/lib/oxidized/model/gaiaos.rb +7 -11
- data/lib/oxidized/model/gcombnps.rb +84 -0
- data/lib/oxidized/model/grandstream.rb +9 -0
- data/lib/oxidized/model/hatteras.rb +9 -6
- data/lib/oxidized/model/hirschmann.rb +39 -0
- data/lib/oxidized/model/hpebladesystem.rb +20 -18
- data/lib/oxidized/model/hpemsa.rb +10 -0
- data/lib/oxidized/model/hpmsm.rb +84 -0
- data/lib/oxidized/model/ibos.rb +55 -0
- data/lib/oxidized/model/icotera.rb +27 -0
- data/lib/oxidized/model/ios.rb +63 -70
- data/lib/oxidized/model/iosxe.rb +5 -0
- data/lib/oxidized/model/iosxr.rb +2 -3
- data/lib/oxidized/model/ipos.rb +10 -6
- data/lib/oxidized/model/ironware.rb +20 -19
- data/lib/oxidized/model/isam.rb +5 -6
- data/lib/oxidized/model/junos.rb +9 -11
- data/lib/oxidized/model/linuxgeneric.rb +74 -0
- data/lib/oxidized/model/masteros.rb +3 -6
- data/lib/oxidized/model/mlnxos.rb +9 -10
- data/lib/oxidized/model/model.rb +72 -46
- data/lib/oxidized/model/mtrlrfs.rb +1 -4
- data/lib/oxidized/model/ndms.rb +23 -0
- data/lib/oxidized/model/netgear.rb +35 -15
- data/lib/oxidized/model/netonix.rb +2 -2
- data/lib/oxidized/model/netscaler.rb +6 -3
- data/lib/oxidized/model/nos.rb +5 -7
- data/lib/oxidized/model/nsxconfig.rb +22 -0
- data/lib/oxidized/model/nsxfirewall.rb +22 -0
- data/lib/oxidized/model/nxos.rb +13 -3
- data/lib/oxidized/model/oneos.rb +15 -9
- data/lib/oxidized/model/openbsd.rb +63 -0
- data/lib/oxidized/model/opengear.rb +3 -5
- data/lib/oxidized/model/openwrt.rb +78 -0
- data/lib/oxidized/model/opnsense.rb +19 -0
- data/lib/oxidized/model/os10.rb +46 -0
- data/lib/oxidized/model/outputs.rb +5 -7
- data/lib/oxidized/model/panos.rb +11 -12
- data/lib/oxidized/model/pfsense.rb +11 -6
- data/lib/oxidized/model/planet.rb +14 -17
- data/lib/oxidized/model/powerconnect.rb +24 -19
- data/lib/oxidized/model/procurve.rb +43 -11
- data/lib/oxidized/model/purityos.rb +12 -0
- data/lib/oxidized/model/qtech.rb +41 -0
- data/lib/oxidized/model/quantaos.rb +4 -6
- data/lib/oxidized/model/raisecom.rb +19 -0
- data/lib/oxidized/model/routeros.rb +26 -8
- data/lib/oxidized/model/saos.rb +1 -2
- data/lib/oxidized/model/screenos.rb +8 -11
- data/lib/oxidized/model/sgos.rb +45 -0
- data/lib/oxidized/model/siklu.rb +1 -3
- data/lib/oxidized/model/slxos.rb +59 -0
- data/lib/oxidized/model/smartax.rb +25 -0
- data/lib/oxidized/model/sonicos.rb +51 -0
- data/lib/oxidized/model/speedtouch.rb +34 -0
- data/lib/oxidized/model/sros.rb +96 -0
- data/lib/oxidized/model/stoneos.rb +32 -0
- data/lib/oxidized/model/supermicro.rb +6 -41
- data/lib/oxidized/model/tdre.rb +30 -0
- data/lib/oxidized/model/telco.rb +24 -0
- data/lib/oxidized/model/timos.rb +6 -114
- data/lib/oxidized/model/tmos.rb +6 -3
- data/lib/oxidized/model/tplink.rb +11 -11
- data/lib/oxidized/model/trango.rb +21 -42
- data/lib/oxidized/model/ucs.rb +30 -0
- data/lib/oxidized/model/viptela.rb +29 -0
- data/lib/oxidized/model/voltaire.rb +9 -12
- data/lib/oxidized/model/voss.rb +17 -6
- data/lib/oxidized/model/vrp.rb +11 -6
- data/lib/oxidized/model/vyatta.rb +8 -6
- data/lib/oxidized/model/weos.rb +20 -0
- data/lib/oxidized/model/xos.rb +20 -8
- data/lib/oxidized/model/zhoneolt.rb +2 -2
- data/lib/oxidized/model/zynos.rb +1 -3
- data/lib/oxidized/model/zynoscli.rb +36 -0
- data/lib/oxidized/model/zynosgs.rb +38 -0
- data/lib/oxidized/node/stats.rb +33 -8
- data/lib/oxidized/node.rb +86 -95
- data/lib/oxidized/nodes.rb +48 -44
- data/lib/oxidized/output/file.rb +32 -37
- data/lib/oxidized/output/git.rb +138 -153
- data/lib/oxidized/output/gitcrypt.rb +228 -242
- data/lib/oxidized/output/http.rb +35 -34
- data/lib/oxidized/output/output.rb +2 -3
- data/lib/oxidized/source/csv.rb +50 -44
- data/lib/oxidized/source/http.rb +58 -58
- data/lib/oxidized/source/source.rb +9 -10
- data/lib/oxidized/source/sql.rb +47 -45
- data/lib/oxidized/string.rb +18 -14
- data/lib/oxidized/version.rb +17 -1
- data/lib/oxidized/worker.rb +72 -33
- data/oxidized.gemspec +20 -19
- metadata +180 -36
- data/.ruby-version +0 -1
- data/Gemfile.lock +0 -44
data/lib/oxidized/input/ssh.rb
CHANGED
@@ -5,61 +5,42 @@ module Oxidized
|
|
5
5
|
require 'oxidized/input/cli'
|
6
6
|
class SSH < Input
|
7
7
|
RescueFail = {
|
8
|
-
:
|
9
|
-
Net::SSH::Disconnect
|
8
|
+
debug: [
|
9
|
+
Net::SSH::Disconnect
|
10
10
|
],
|
11
|
-
:
|
11
|
+
warn: [
|
12
12
|
RuntimeError,
|
13
|
-
Net::SSH::AuthenticationFailed
|
14
|
-
]
|
15
|
-
}
|
13
|
+
Net::SSH::AuthenticationFailed
|
14
|
+
]
|
15
|
+
}.freeze
|
16
16
|
include Input::CLI
|
17
17
|
class NoShell < OxidizedError; end
|
18
18
|
|
19
|
-
def connect
|
19
|
+
def connect(node)
|
20
20
|
@node = node
|
21
21
|
@output = ''
|
22
22
|
@pty_options = { term: "vt100" }
|
23
23
|
@node.model.cfg['ssh'].each { |cb| instance_exec(&cb) }
|
24
|
-
secure = Oxidized.config.input.ssh.secure
|
25
24
|
@log = File.open(Oxidized::Config::Log + "/#{@node.ip}-ssh", 'w') if Oxidized.config.input.debug?
|
26
|
-
port = vars(:ssh_port) || 22
|
27
|
-
if proxy_host = vars(:ssh_proxy)
|
28
|
-
proxy_command = "ssh "
|
29
|
-
proxy_command += "-o StrictHostKeyChecking=no " unless secure
|
30
|
-
proxy_command += "#{proxy_host} -W %h:%p"
|
31
|
-
proxy = Net::SSH::Proxy::Command.new(proxy_command)
|
32
|
-
end
|
33
|
-
ssh_opts = {
|
34
|
-
:port => port.to_i,
|
35
|
-
:password => @node.auth[:password], :timeout => Oxidized.config.timeout,
|
36
|
-
:paranoid => secure,
|
37
|
-
:auth_methods => %w(none publickey password keyboard-interactive),
|
38
|
-
:number_of_password_prompts => 0,
|
39
|
-
:proxy => proxy,
|
40
|
-
}
|
41
|
-
ssh_opts[:keys] = vars(:ssh_keys).is_a?(Array) ? vars(:ssh_keys) : [vars(:ssh_keys)] if vars(:ssh_keys)
|
42
|
-
ssh_opts[:kex] = vars(:ssh_kex).split(/,\s*/) if vars(:ssh_kex)
|
43
|
-
ssh_opts[:encryption] = vars(:ssh_encryption).split(/,\s*/) if vars(:ssh_encryption)
|
44
25
|
|
45
26
|
Oxidized.logger.debug "lib/oxidized/input/ssh.rb: Connecting to #{@node.name}"
|
46
|
-
@ssh = Net::SSH.start(@node.ip, @node.auth[:username],
|
27
|
+
@ssh = Net::SSH.start(@node.ip, @node.auth[:username], make_ssh_opts)
|
47
28
|
unless @exec
|
48
29
|
shell_open @ssh
|
49
30
|
begin
|
50
31
|
login
|
51
32
|
rescue Timeout::Error
|
52
|
-
raise PromptUndetect, [
|
33
|
+
raise PromptUndetect, [@output, 'not matching configured prompt', @node.prompt].join(' ')
|
53
34
|
end
|
54
35
|
end
|
55
36
|
connected?
|
56
37
|
end
|
57
38
|
|
58
39
|
def connected?
|
59
|
-
@ssh
|
40
|
+
@ssh && (not @ssh.closed?)
|
60
41
|
end
|
61
42
|
|
62
|
-
def cmd
|
43
|
+
def cmd(cmd, expect = node.prompt)
|
63
44
|
Oxidized.logger.debug "lib/oxidized/input/ssh.rb #{cmd} @ #{node.name} with expect: #{expect.inspect}"
|
64
45
|
if @exec
|
65
46
|
@ssh.exec! cmd
|
@@ -68,15 +49,13 @@ module Oxidized
|
|
68
49
|
end
|
69
50
|
end
|
70
51
|
|
71
|
-
def send
|
52
|
+
def send(data)
|
72
53
|
@ses.send_data data
|
73
54
|
end
|
74
55
|
|
75
|
-
|
76
|
-
@output
|
77
|
-
end
|
56
|
+
attr_reader :output
|
78
57
|
|
79
|
-
def pty_options
|
58
|
+
def pty_options(hash)
|
80
59
|
@pty_options = @pty_options.merge hash
|
81
60
|
end
|
82
61
|
|
@@ -85,25 +64,26 @@ module Oxidized
|
|
85
64
|
def disconnect
|
86
65
|
disconnect_cli
|
87
66
|
# if disconnect does not disconnect us, give up after timeout
|
88
|
-
Timeout
|
67
|
+
Timeout.timeout(Oxidized.config.timeout) { @ssh.loop }
|
89
68
|
rescue Errno::ECONNRESET, Net::SSH::Disconnect, IOError
|
90
69
|
ensure
|
91
70
|
@log.close if Oxidized.config.input.debug?
|
92
71
|
(@ssh.close rescue true) unless @ssh.closed?
|
93
72
|
end
|
94
73
|
|
95
|
-
def shell_open
|
74
|
+
def shell_open(ssh)
|
96
75
|
@ses = ssh.open_channel do |ch|
|
97
76
|
ch.on_data do |_ch, data|
|
98
77
|
if Oxidized.config.input.debug?
|
99
78
|
@log.print data
|
100
|
-
@log.
|
79
|
+
@log.flush
|
101
80
|
end
|
102
81
|
@output << data
|
103
82
|
@output = @node.model.expects @output
|
104
83
|
end
|
105
|
-
ch.request_pty
|
84
|
+
ch.request_pty(@pty_options) do |_ch, success_pty|
|
106
85
|
raise NoShell, "Can't get PTY" unless success_pty
|
86
|
+
|
107
87
|
ch.send_channel_request 'shell' do |_ch, success_shell|
|
108
88
|
raise NoShell, "Can't get shell" unless success_shell
|
109
89
|
end
|
@@ -111,22 +91,10 @@ module Oxidized
|
|
111
91
|
end
|
112
92
|
end
|
113
93
|
|
114
|
-
|
115
|
-
|
116
|
-
def login
|
117
|
-
if @username
|
118
|
-
match = expect username, @node.prompt
|
119
|
-
if match == username
|
120
|
-
cmd @node.auth[:username], password
|
121
|
-
cmd @node.auth[:password]
|
122
|
-
end
|
123
|
-
else
|
124
|
-
expect @node.prompt
|
125
|
-
end
|
126
|
-
end
|
94
|
+
def exec(state = nil)
|
95
|
+
return nil if vars(:ssh_no_exec)
|
127
96
|
|
128
|
-
|
129
|
-
state == nil ? @exec : (@exec=state) unless vars :ssh_no_exec
|
97
|
+
state.nil? ? @exec : (@exec = state)
|
130
98
|
end
|
131
99
|
|
132
100
|
def cmd_shell(cmd, expect_re)
|
@@ -137,18 +105,58 @@ module Oxidized
|
|
137
105
|
@output
|
138
106
|
end
|
139
107
|
|
140
|
-
def expect
|
108
|
+
def expect(*regexps)
|
141
109
|
regexps = [regexps].flatten
|
142
110
|
Oxidized.logger.debug "lib/oxidized/input/ssh.rb: expecting #{regexps.inspect} at #{node.name}"
|
143
|
-
Timeout
|
111
|
+
Timeout.timeout(Oxidized.config.timeout) do
|
144
112
|
@ssh.loop(0.1) do
|
145
113
|
sleep 0.1
|
146
114
|
match = regexps.find { |regexp| @output.match regexp }
|
147
115
|
return match if match
|
116
|
+
|
148
117
|
true
|
149
118
|
end
|
150
119
|
end
|
151
120
|
end
|
152
121
|
|
122
|
+
def make_ssh_opts
|
123
|
+
secure = Oxidized.config.input.ssh.secure?
|
124
|
+
ssh_opts = {
|
125
|
+
number_of_password_prompts: 0,
|
126
|
+
keepalive: vars(:ssh_no_keepalive) ? false : true,
|
127
|
+
verify_host_key: secure ? :always : :never,
|
128
|
+
password: @node.auth[:password],
|
129
|
+
timeout: Oxidized.config.timeout,
|
130
|
+
port: (vars(:ssh_port) || 22).to_i
|
131
|
+
}
|
132
|
+
|
133
|
+
auth_methods = vars(:auth_methods) || %w[none publickey password]
|
134
|
+
ssh_opts[:auth_methods] = auth_methods
|
135
|
+
Oxidized.logger.debug "AUTH METHODS::#{auth_methods}"
|
136
|
+
|
137
|
+
if (proxy_host = vars(:ssh_proxy))
|
138
|
+
proxy_command = "ssh "
|
139
|
+
proxy_command += "-o StrictHostKeyChecking=no " unless secure
|
140
|
+
if (proxy_port = vars(:ssh_proxy_port))
|
141
|
+
proxy_command += "-p #{proxy_port} "
|
142
|
+
end
|
143
|
+
proxy_command += "#{proxy_host} -W %h:%p"
|
144
|
+
proxy = Net::SSH::Proxy::Command.new(proxy_command)
|
145
|
+
ssh_opts[:proxy] = proxy
|
146
|
+
end
|
147
|
+
|
148
|
+
ssh_opts[:keys] = [vars(:ssh_keys)].flatten if vars(:ssh_keys)
|
149
|
+
ssh_opts[:kex] = vars(:ssh_kex).split(/,\s*/) if vars(:ssh_kex)
|
150
|
+
ssh_opts[:encryption] = vars(:ssh_encryption).split(/,\s*/) if vars(:ssh_encryption)
|
151
|
+
ssh_opts[:host_key] = vars(:ssh_host_key).split(/,\s*/) if vars(:ssh_host_key)
|
152
|
+
ssh_opts[:hmac] = vars(:ssh_hmac).split(/,\s*/) if vars(:ssh_hmac)
|
153
|
+
|
154
|
+
if Oxidized.config.input.debug?
|
155
|
+
ssh_opts[:logger] = Oxidized.logger
|
156
|
+
ssh_opts[:verbose] = Logger::DEBUG
|
157
|
+
end
|
158
|
+
|
159
|
+
ssh_opts
|
160
|
+
end
|
153
161
|
end
|
154
162
|
end
|
@@ -2,48 +2,49 @@ module Oxidized
|
|
2
2
|
require 'net/telnet'
|
3
3
|
require 'oxidized/input/cli'
|
4
4
|
class Telnet < Input
|
5
|
-
RescueFail = {}
|
5
|
+
RescueFail = {}.freeze
|
6
6
|
include Input::CLI
|
7
7
|
attr_reader :telnet
|
8
8
|
|
9
|
-
def connect
|
9
|
+
def connect(node)
|
10
10
|
@node = node
|
11
11
|
@timeout = Oxidized.config.timeout
|
12
12
|
@node.model.cfg['telnet'].each { |cb| instance_exec(&cb) }
|
13
|
+
@log = File.open(Oxidized::Config::Log + "/#{@node.ip}-telnet", 'w') if Oxidized.config.input.debug?
|
13
14
|
port = vars(:telnet_port) || 23
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
16
|
+
telnet_opts = {
|
17
|
+
'Host' => @node.ip,
|
18
|
+
'Port' => port.to_i,
|
19
|
+
'Timeout' => @timeout,
|
20
|
+
'Model' => @node.model,
|
21
|
+
'Log' => @log
|
22
|
+
}
|
20
23
|
|
21
|
-
@telnet
|
22
|
-
if @node.auth[:username] and @node.auth[:username].length > 0
|
23
|
-
expect username
|
24
|
-
@telnet.puts @node.auth[:username]
|
25
|
-
end
|
26
|
-
expect password
|
27
|
-
@telnet.puts @node.auth[:password]
|
24
|
+
@telnet = Net::Telnet.new telnet_opts
|
28
25
|
begin
|
29
|
-
|
26
|
+
login
|
30
27
|
rescue Timeout::Error
|
31
|
-
raise PromptUndetect, [
|
28
|
+
raise PromptUndetect, ['unable to detect prompt:', @node.prompt].join(' ')
|
32
29
|
end
|
30
|
+
connected?
|
33
31
|
end
|
34
32
|
|
35
33
|
def connected?
|
36
|
-
@telnet
|
34
|
+
@telnet && (not @telnet.sock.closed?)
|
37
35
|
end
|
38
36
|
|
39
|
-
def cmd
|
40
|
-
|
41
|
-
|
42
|
-
|
37
|
+
def cmd(cmd_str, expect = @node.prompt)
|
38
|
+
return send(cmd_str + "\r\n") unless expect
|
39
|
+
|
40
|
+
Oxidized.logger.debug "Telnet: #{cmd_str} @#{@node.name}"
|
41
|
+
args = { 'String' => cmd_str,
|
42
|
+
'Match' => expect,
|
43
|
+
'Timeout' => @timeout }
|
43
44
|
@telnet.cmd args
|
44
45
|
end
|
45
46
|
|
46
|
-
def send
|
47
|
+
def send(data)
|
47
48
|
@telnet.write data
|
48
49
|
end
|
49
50
|
|
@@ -53,106 +54,62 @@ module Oxidized
|
|
53
54
|
|
54
55
|
private
|
55
56
|
|
56
|
-
def expect
|
57
|
-
@telnet.
|
57
|
+
def expect(regex)
|
58
|
+
@telnet.oxidized_expect expect: regex, timeout: @timeout
|
58
59
|
end
|
59
60
|
|
60
61
|
def disconnect
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
62
|
+
disconnect_cli
|
63
|
+
@telnet.close
|
64
|
+
rescue Errno::ECONNRESET
|
65
|
+
ensure
|
66
|
+
@log.close if Oxidized.config.input.debug?
|
67
|
+
(@telnet.close rescue true) unless @telnet.sock.closed?
|
66
68
|
end
|
67
|
-
|
68
69
|
end
|
69
70
|
end
|
70
71
|
|
71
|
-
|
72
72
|
class Net::Telnet
|
73
|
-
## FIXME: we just need 'line = model.expects line' to handle pager
|
74
73
|
## how to do this, without redefining the whole damn thing
|
75
74
|
## FIXME: we also need output (not sure I'm going to support this)
|
76
75
|
attr_reader :output
|
77
|
-
def
|
78
|
-
time_out = @options["Timeout"]
|
79
|
-
waittime = @options["Waittime"]
|
80
|
-
fail_eof = @options["FailEOF"]
|
76
|
+
def oxidized_expect(options)
|
81
77
|
model = @options["Model"]
|
78
|
+
@log = @options["Log"]
|
82
79
|
|
83
|
-
|
84
|
-
|
85
|
-
options["Match"]
|
86
|
-
elsif options.has_key?("Prompt")
|
87
|
-
options["Prompt"]
|
88
|
-
elsif options.has_key?("String")
|
89
|
-
Regexp.new( Regexp.quote(options["String"]) )
|
90
|
-
end
|
91
|
-
time_out = options["Timeout"] if options.has_key?("Timeout")
|
92
|
-
waittime = options["Waittime"] if options.has_key?("Waittime")
|
93
|
-
fail_eof = options["FailEOF"] if options.has_key?("FailEOF")
|
94
|
-
else
|
95
|
-
prompt = options
|
96
|
-
end
|
80
|
+
expects = [options[:expect]].flatten
|
81
|
+
time_out = options[:timeout] || @options["Timeout"] || Oxidized.config.timeout?
|
97
82
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
buf = ''
|
104
|
-
rest = ''
|
105
|
-
until(prompt === line and not IO::select([@sock], nil, nil, waittime))
|
106
|
-
unless IO::select([@sock], nil, nil, time_out)
|
107
|
-
raise TimeoutError, "timed out while waiting for more data"
|
108
|
-
end
|
109
|
-
begin
|
83
|
+
Timeout.timeout(time_out) do
|
84
|
+
line = ""
|
85
|
+
rest = ""
|
86
|
+
buf = ""
|
87
|
+
loop do
|
110
88
|
c = @sock.readpartial(1024 * 1024)
|
111
89
|
@output = c
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
#
|
130
|
-
# We cannot use preprocess() on this data, because that
|
131
|
-
# method makes some Telnetmode-specific assumptions.
|
132
|
-
buf = rest + c
|
133
|
-
rest = ''
|
134
|
-
unless @options["Binmode"]
|
135
|
-
if pt = buf.rindex(/\r\z/no)
|
136
|
-
buf = buf[0 ... pt]
|
137
|
-
rest = buf[pt .. -1]
|
138
|
-
end
|
139
|
-
buf.gsub!(/#{EOL}/no, "\n")
|
140
|
-
end
|
90
|
+
c = rest + c
|
91
|
+
|
92
|
+
if Integer(c.rindex(/#{IAC}#{SE}/no) || 0) <
|
93
|
+
Integer(c.rindex(/#{IAC}#{SB}/no) || 0)
|
94
|
+
buf = preprocess(c[0...c.rindex(/#{IAC}#{SB}/no)])
|
95
|
+
rest = c[c.rindex(/#{IAC}#{SB}/no)..-1]
|
96
|
+
elsif (pt = c.rindex(/#{IAC}[^#{IAC}#{AO}#{AYT}#{DM}#{IP}#{NOP}]?\z/no) ||
|
97
|
+
c.rindex(/\r\z/no))
|
98
|
+
buf = preprocess(c[0...pt])
|
99
|
+
rest = c[pt..-1]
|
100
|
+
else
|
101
|
+
buf = preprocess(c)
|
102
|
+
rest = ''
|
103
|
+
end
|
104
|
+
if Oxidized.config.input.debug?
|
105
|
+
@log.print buf
|
106
|
+
@log.flush
|
141
107
|
end
|
142
|
-
@log.print(buf) if @options.has_key?("Output_log")
|
143
108
|
line += buf
|
144
109
|
line = model.expects line
|
145
|
-
|
146
|
-
|
147
|
-
rescue EOFError # End of file reached
|
148
|
-
raise if fail_eof
|
149
|
-
if line == ''
|
150
|
-
line = nil
|
151
|
-
yield nil if block_given?
|
152
|
-
end
|
153
|
-
break
|
110
|
+
match = expects.find { |re| line.match re }
|
111
|
+
return match if match
|
154
112
|
end
|
155
113
|
end
|
156
|
-
line
|
157
114
|
end
|
158
115
|
end
|
data/lib/oxidized/input/tftp.rb
CHANGED
@@ -1,41 +1,40 @@
|
|
1
1
|
module Oxidized
|
2
2
|
require 'stringio'
|
3
3
|
require_relative 'cli'
|
4
|
-
|
4
|
+
|
5
5
|
begin
|
6
6
|
require 'net/tftp'
|
7
7
|
rescue LoadError
|
8
8
|
raise OxidizedError, 'net/tftp not found: sudo gem install net-tftp'
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
class TFTP < Input
|
12
|
-
|
13
12
|
include Input::CLI
|
14
|
-
|
13
|
+
|
15
14
|
# TFTP utilizes UDP, there is not a connection. We simply specify an IP and send/receive data.
|
16
|
-
def connect
|
17
|
-
@node
|
15
|
+
def connect(node)
|
16
|
+
@node = node
|
18
17
|
|
19
18
|
@node.model.cfg['tftp'].each { |cb| instance_exec(&cb) }
|
20
19
|
@log = File.open(Oxidized::Config::Log + "/#{@node.ip}-tftp", 'w') if Oxidized.config.input.debug?
|
21
20
|
@tftp = Net::TFTP.new @node.ip
|
22
21
|
end
|
23
22
|
|
24
|
-
def cmd
|
23
|
+
def cmd(file)
|
25
24
|
Oxidized.logger.debug "TFTP: #{file} @ #{@node.name}"
|
26
25
|
config = StringIO.new
|
27
26
|
@tftp.getbinary file, config
|
28
27
|
config.rewind
|
29
28
|
config.read
|
30
29
|
end
|
31
|
-
|
30
|
+
|
32
31
|
private
|
33
|
-
|
32
|
+
|
34
33
|
def disconnect
|
35
34
|
# TFTP uses UDP, there is no connection to close
|
35
|
+
true
|
36
36
|
ensure
|
37
37
|
@log.close if Oxidized.config.input.debug?
|
38
38
|
end
|
39
|
-
|
40
39
|
end
|
41
40
|
end
|
data/lib/oxidized/jobs.rb
CHANGED
@@ -4,11 +4,11 @@ module Oxidized
|
|
4
4
|
MAX_INTER_JOB_GAP = 300 # add job if more than X from last job started
|
5
5
|
attr_accessor :interval, :max, :want
|
6
6
|
|
7
|
-
def initialize
|
7
|
+
def initialize(max, interval, nodes)
|
8
8
|
@max = max
|
9
|
-
# Set interval to 1 if interval is 0 (=disabled) so we don't break
|
9
|
+
# Set interval to 1 if interval is 0 (=disabled) so we don't break
|
10
10
|
# the 'ceil' function
|
11
|
-
@interval = interval
|
11
|
+
@interval = interval.zero? ? 1 : interval
|
12
12
|
@nodes = nodes
|
13
13
|
@last = Time.now.utc
|
14
14
|
@durations = Array.new @nodes.size, AVERAGE_DURATION
|
@@ -16,19 +16,19 @@ module Oxidized
|
|
16
16
|
super()
|
17
17
|
end
|
18
18
|
|
19
|
-
def push
|
19
|
+
def push(arg)
|
20
20
|
@last = Time.now.utc
|
21
21
|
super
|
22
22
|
end
|
23
23
|
|
24
|
-
def duration
|
24
|
+
def duration(last)
|
25
25
|
if @durations.size > @nodes.size
|
26
26
|
@durations.slice! @nodes.size...@durations.size
|
27
27
|
elsif @durations.size < @nodes.size
|
28
28
|
@durations.fill AVERAGE_DURATION, @durations.size...@nodes.size
|
29
29
|
end
|
30
30
|
@durations.push(last).shift
|
31
|
-
@duration = @durations.inject(:+).to_f / @nodes.size #rolling average
|
31
|
+
@duration = @durations.inject(:+).to_f / @nodes.size # rolling average
|
32
32
|
new_count
|
33
33
|
end
|
34
34
|
|
@@ -44,10 +44,9 @@ module Oxidized
|
|
44
44
|
# and b) we want less threads running than the total amount of nodes
|
45
45
|
# and c) there is more than MAX_INTER_JOB_GAP since last one was started
|
46
46
|
# then we want one more thread (rationale is to fix hanging thread causing HOLB)
|
47
|
-
|
48
|
-
@want +=1 if (Time.now.utc - @last) > MAX_INTER_JOB_GAP
|
49
|
-
end
|
50
|
-
end
|
47
|
+
return unless @want <= size && @want < @nodes.size
|
51
48
|
|
49
|
+
@want += 1 if (Time.now.utc - @last) > MAX_INTER_JOB_GAP
|
50
|
+
end
|
52
51
|
end
|
53
52
|
end
|
data/lib/oxidized/manager.rb
CHANGED
@@ -5,62 +5,60 @@ module Oxidized
|
|
5
5
|
require 'oxidized/source/source'
|
6
6
|
class Manager
|
7
7
|
class << self
|
8
|
-
def load
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
break if klass
|
17
|
-
end
|
18
|
-
i = klass.new
|
19
|
-
i.setup if i.respond_to? :setup
|
20
|
-
{ file => klass }
|
21
|
-
rescue LoadError
|
22
|
-
{}
|
8
|
+
def load(dir, file)
|
9
|
+
require File.join dir, file + '.rb'
|
10
|
+
klass = nil
|
11
|
+
[Oxidized, Object].each do |mod|
|
12
|
+
klass = mod.constants.find { |const| const.to_s.casecmp(file).zero? }
|
13
|
+
klass ||= mod.constants.find { |const| const.to_s.downcase == 'oxidized' + file.downcase }
|
14
|
+
klass = mod.const_get klass if klass
|
15
|
+
break if klass
|
23
16
|
end
|
17
|
+
i = klass.new
|
18
|
+
i.setup if i.respond_to? :setup
|
19
|
+
{ file => klass }
|
20
|
+
rescue LoadError
|
21
|
+
false
|
24
22
|
end
|
25
23
|
end
|
26
|
-
|
24
|
+
|
25
|
+
attr_reader :input, :output, :source, :model, :hook
|
27
26
|
def initialize
|
28
27
|
@input = {}
|
29
28
|
@output = {}
|
30
|
-
@model = {}
|
31
29
|
@source = {}
|
32
|
-
@
|
30
|
+
@model = {}
|
31
|
+
@hook = {}
|
32
|
+
end
|
33
|
+
|
34
|
+
def add_input(name)
|
35
|
+
loader @input, Config::InputDir, "input", name
|
33
36
|
end
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
@input.merge! method
|
37
|
+
|
38
|
+
def add_output(name)
|
39
|
+
loader @output, Config::OutputDir, "output", name
|
38
40
|
end
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
@output.merge! method
|
41
|
+
|
42
|
+
def add_source(name)
|
43
|
+
loader @source, Config::SourceDir, "source", name
|
43
44
|
end
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
_model = Manager.load Config::ModelDir, name if _model.empty?
|
48
|
-
return false if _model.empty?
|
49
|
-
@model.merge! _model
|
45
|
+
|
46
|
+
def add_model(name)
|
47
|
+
loader @model, Config::ModelDir, "model", name
|
50
48
|
end
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
return false if _source.empty?
|
55
|
-
@source.merge! _source
|
49
|
+
|
50
|
+
def add_hook(name)
|
51
|
+
loader @hook, Config::HookDir, "hook", name
|
56
52
|
end
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
# if local version of file exists, load it, else load global - return falsy value if nothing loaded
|
57
|
+
def loader(hash, global_dir, local_dir, name)
|
58
|
+
dir = File.join(Config::Root, local_dir)
|
59
|
+
map = Manager.load(dir, name) if File.exist? File.join(dir, name + ".rb")
|
60
|
+
map ||= Manager.load(global_dir, name)
|
61
|
+
hash.merge!(map) if map
|
64
62
|
end
|
65
63
|
end
|
66
64
|
end
|