oxidized 0.21.0 → 0.22.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/no-response.yml +13 -0
- data/.gitignore +3 -0
- data/.rubocop.yml +37 -0
- data/.rubocop_todo.yml +714 -0
- data/.travis.yml +7 -1
- data/CHANGELOG.md +341 -243
- data/Dockerfile +44 -16
- data/LICENSE +201 -0
- data/README.md +114 -82
- data/Rakefile +19 -0
- data/TODO.md +29 -23
- data/bin/oxidized +1 -2
- data/docs/Configuration.md +71 -31
- data/docs/Creating-Models.md +78 -0
- data/docs/Hooks.md +145 -41
- data/docs/Model-Notes/AireOS.md +12 -0
- data/docs/Model-Notes/ArbOS.md +12 -0
- data/docs/Model-Notes/Comware.md +14 -0
- data/docs/Model-Notes/EOS.md +9 -0
- data/docs/Model-Notes/JunOS.md +34 -0
- data/docs/Model-Notes/Netgear.md +68 -0
- data/docs/Model-Notes/README.md +19 -0
- data/docs/{VRP-Huawei.md → Model-Notes/VRP-Huawei.md} +10 -2
- data/docs/Model-Notes/XGS4600-Zyxel.md +39 -0
- data/docs/Outputs.md +27 -28
- data/docs/Ruby-API.md +38 -18
- data/docs/Sources.md +78 -16
- data/docs/Supported-OS-Types.md +171 -148
- data/extra/oxidized.logrotate +7 -0
- data/extra/oxidized.service +1 -1
- data/extra/rest_client.rb +4 -5
- data/extra/syslog.rb +16 -16
- data/lib/oxidized/cli.rb +3 -3
- data/lib/oxidized/config.rb +7 -4
- data/lib/oxidized/core.rb +3 -3
- data/lib/oxidized/hook.rb +64 -65
- data/lib/oxidized/hook/awssns.rb +2 -3
- data/lib/oxidized/hook/ciscosparkdiff.rb +49 -0
- data/lib/oxidized/hook/exec.rb +5 -5
- data/lib/oxidized/hook/githubrepo.rb +20 -14
- data/lib/oxidized/hook/slackdiff.rb +38 -19
- data/lib/oxidized/hook/xmppdiff.rb +58 -0
- data/lib/oxidized/input/cli.rb +5 -6
- data/lib/oxidized/input/ftp.rb +8 -7
- data/lib/oxidized/input/http.rb +39 -0
- data/lib/oxidized/input/ssh.rb +24 -22
- data/lib/oxidized/input/telnet.rb +38 -32
- data/lib/oxidized/jobs.rb +3 -4
- data/lib/oxidized/manager.rb +9 -4
- data/lib/oxidized/model/acos.rb +15 -16
- data/lib/oxidized/model/acsw.rb +3 -8
- data/lib/oxidized/model/aen.rb +1 -2
- data/lib/oxidized/model/aireos.rb +3 -5
- data/lib/oxidized/model/alteonos.rb +16 -18
- data/lib/oxidized/model/alvarion.rb +0 -4
- data/lib/oxidized/model/aos.rb +2 -4
- data/lib/oxidized/model/aos7.rb +2 -3
- data/lib/oxidized/model/aosw.rb +13 -15
- data/lib/oxidized/model/apc_aos.rb +0 -3
- data/lib/oxidized/model/arbos.rb +26 -0
- data/lib/oxidized/model/aricentiss.rb +51 -0
- data/lib/oxidized/model/asa.rb +33 -35
- data/lib/oxidized/model/asyncos.rb +41 -44
- data/lib/oxidized/model/audiocodes.rb +4 -8
- data/lib/oxidized/model/awplus.rb +84 -0
- data/lib/oxidized/model/boss.rb +6 -5
- data/lib/oxidized/model/br6910.rb +43 -45
- data/lib/oxidized/model/c4cmts.rb +3 -5
- data/lib/oxidized/model/cambium.rb +22 -0
- data/lib/oxidized/model/catos.rb +0 -2
- data/lib/oxidized/model/cisconga.rb +1 -3
- data/lib/oxidized/model/ciscosma.rb +37 -40
- data/lib/oxidized/model/ciscosmb.rb +7 -4
- data/lib/oxidized/model/comnetms.rb +43 -0
- data/lib/oxidized/model/comware.rb +9 -9
- data/lib/oxidized/model/coriant8600.rb +3 -5
- data/lib/oxidized/model/coriantgroove.rb +3 -5
- data/lib/oxidized/model/corianttmos.rb +1 -3
- data/lib/oxidized/model/cumulus.rb +26 -32
- data/lib/oxidized/model/datacom.rb +0 -2
- data/lib/oxidized/model/dcnos.rb +46 -0
- data/lib/oxidized/model/dlink.rb +1 -1
- data/lib/oxidized/model/dnos.rb +9 -5
- data/lib/oxidized/model/edgecos.rb +45 -0
- data/lib/oxidized/model/edgeos.rb +5 -3
- data/lib/oxidized/model/edgeswitch.rb +1 -3
- data/lib/oxidized/model/enterasys.rb +1 -3
- data/lib/oxidized/model/eos.rb +6 -8
- data/lib/oxidized/model/fabricos.rb +3 -5
- data/lib/oxidized/model/firewareos.rb +2 -5
- data/lib/oxidized/model/fortios.rb +21 -17
- data/lib/oxidized/model/ftos.rb +2 -4
- data/lib/oxidized/model/fujitsupy.rb +2 -4
- data/lib/oxidized/model/gaiaos.rb +6 -10
- data/lib/oxidized/model/gcombnps.rb +82 -0
- data/lib/oxidized/model/hatteras.rb +8 -5
- data/lib/oxidized/model/hirschmann.rb +8 -10
- data/lib/oxidized/model/hpebladesystem.rb +19 -17
- data/lib/oxidized/model/hpemsa.rb +0 -3
- data/lib/oxidized/model/ios.rb +54 -55
- data/lib/oxidized/model/iosxe.rb +5 -0
- data/lib/oxidized/model/iosxr.rb +1 -3
- data/lib/oxidized/model/ipos.rb +1 -3
- data/lib/oxidized/model/ironware.rb +12 -15
- data/lib/oxidized/model/isam.rb +4 -5
- data/lib/oxidized/model/junos.rb +8 -7
- data/lib/oxidized/model/masteros.rb +1 -3
- data/lib/oxidized/model/mlnxos.rb +3 -4
- data/lib/oxidized/model/model.rb +15 -7
- data/lib/oxidized/model/mtrlrfs.rb +1 -4
- data/lib/oxidized/model/ndms.rb +24 -0
- data/lib/oxidized/model/netgear.rb +3 -4
- data/lib/oxidized/model/netscaler.rb +0 -2
- data/lib/oxidized/model/nos.rb +1 -3
- data/lib/oxidized/model/nxos.rb +13 -3
- data/lib/oxidized/model/oneos.rb +6 -8
- data/lib/oxidized/model/openbsd.rb +76 -0
- data/lib/oxidized/model/opengear.rb +3 -5
- data/lib/oxidized/model/openwrt.rb +77 -0
- data/lib/oxidized/model/opnsense.rb +19 -0
- data/lib/oxidized/model/outputs.rb +1 -3
- data/lib/oxidized/model/panos.rb +1 -2
- data/lib/oxidized/model/pfsense.rb +9 -5
- data/lib/oxidized/model/planet.rb +8 -12
- data/lib/oxidized/model/powerconnect.rb +6 -9
- data/lib/oxidized/model/procurve.rb +18 -4
- data/lib/oxidized/model/quantaos.rb +3 -5
- data/lib/oxidized/model/routeros.rb +3 -2
- data/lib/oxidized/model/saos.rb +0 -1
- data/lib/oxidized/model/screenos.rb +3 -5
- data/lib/oxidized/model/sgos.rb +2 -3
- data/lib/oxidized/model/siklu.rb +0 -2
- data/lib/oxidized/model/slxos.rb +59 -0
- data/lib/oxidized/model/sros.rb +117 -0
- data/lib/oxidized/model/stoneos.rb +32 -0
- data/lib/oxidized/model/supermicro.rb +6 -41
- data/lib/oxidized/model/timos.rb +6 -114
- data/lib/oxidized/model/tmos.rb +1 -3
- data/lib/oxidized/model/tplink.rb +7 -11
- data/lib/oxidized/model/trango.rb +6 -7
- data/lib/oxidized/model/ucs.rb +0 -1
- data/lib/oxidized/model/voltaire.rb +3 -6
- data/lib/oxidized/model/voss.rb +1 -2
- data/lib/oxidized/model/vrp.rb +4 -5
- data/lib/oxidized/model/vyatta.rb +6 -4
- data/lib/oxidized/model/weos.rb +1 -3
- data/lib/oxidized/model/xos.rb +6 -5
- 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/node.rb +11 -11
- data/lib/oxidized/node/stats.rb +15 -2
- data/lib/oxidized/nodes.rb +8 -8
- data/lib/oxidized/output/file.rb +41 -42
- data/lib/oxidized/output/git.rb +113 -115
- data/lib/oxidized/output/gitcrypt.rb +241 -242
- data/lib/oxidized/output/http.rb +23 -27
- data/lib/oxidized/output/output.rb +1 -2
- data/lib/oxidized/source/csv.rb +44 -45
- data/lib/oxidized/source/http.rb +52 -49
- data/lib/oxidized/source/source.rb +6 -7
- data/lib/oxidized/source/sql.rb +55 -51
- data/lib/oxidized/string.rb +3 -4
- data/lib/oxidized/version.rb +17 -1
- data/lib/oxidized/worker.rb +12 -3
- data/oxidized.gemspec +19 -13
- metadata +139 -51
- data/.ruby-version +0 -1
- data/Gemfile.lock +0 -44
@@ -35,26 +35,32 @@ class GithubRepo < Oxidized::Hook
|
|
35
35
|
end
|
36
36
|
|
37
37
|
Rugged::Commit.create(repo, {
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
38
|
+
parents: [repo.head.target, their_branch.target],
|
39
|
+
tree: merge_index.write_tree(repo),
|
40
|
+
message: "Merge remote-tracking branch '#{their_branch.name}'",
|
41
|
+
update_ref: "HEAD"
|
42
|
+
})
|
43
43
|
end
|
44
44
|
|
45
45
|
private
|
46
46
|
|
47
47
|
def credentials
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
48
|
+
Proc.new do |url, username_from_url, allowed_types|
|
49
|
+
if cfg.has_key?('username')
|
50
|
+
git_user = cfg.username
|
51
|
+
else
|
52
|
+
git_user = username_from_url ? username_from_url : 'git'
|
53
|
+
end
|
54
|
+
|
55
|
+
if cfg.has_key?('password')
|
56
|
+
log "Authenticating using username and password as '#{git_user}'", :debug
|
57
|
+
Rugged::Credentials::UserPassword.new(username: git_user, password: cfg.password)
|
58
|
+
elsif cfg.has_key?('publickey') && cfg.has_key?('privatekey')
|
59
|
+
log "Authenticating using ssh keys as '#{git_user}'", :debug
|
60
|
+
Rugged::Credentials::SshKey.new(username: git_user, publickey: File.expand_path(cfg.publickey), privatekey: File.expand_path(cfg.privatekey), passphrase: ENV["OXIDIZED_SSH_PASSPHRASE"])
|
55
61
|
else
|
56
|
-
log "
|
57
|
-
Rugged::Credentials::SshKeyFromAgent.new(username:
|
62
|
+
log "Authenticating using ssh agent as '#{git_user}'", :debug
|
63
|
+
Rugged::Credentials::SshKeyFromAgent.new(username: git_user)
|
58
64
|
end
|
59
65
|
end
|
60
66
|
end
|
@@ -1,5 +1,8 @@
|
|
1
1
|
require 'slack'
|
2
2
|
|
3
|
+
# defaults to posting a diff, if messageformat is supplied them a message will be posted too
|
4
|
+
# diffenable defaults to true
|
5
|
+
|
3
6
|
class SlackDiff < Oxidized::Hook
|
4
7
|
def validate_cfg!
|
5
8
|
raise KeyError, 'hook.token is required' unless cfg.has_key?('token')
|
@@ -7,28 +10,44 @@ class SlackDiff < Oxidized::Hook
|
|
7
10
|
end
|
8
11
|
|
9
12
|
def run_hook(ctx)
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
13
|
+
return unless ctx.node
|
14
|
+
return unless ctx.event.to_s == "post_store"
|
15
|
+
log "Connecting to slack"
|
16
|
+
Slack.configure do |config|
|
17
|
+
config.token = cfg.token
|
18
|
+
config.proxy = cfg.proxy if cfg.has_key?('proxy')
|
19
|
+
end
|
20
|
+
client = Slack::Client.new
|
21
|
+
client.auth_test
|
22
|
+
log "Connected"
|
23
|
+
# diff snippet - default
|
24
|
+
diffenable = true
|
25
|
+
if cfg.has_key?('diff') == true
|
26
|
+
if cfg.diff == false
|
27
|
+
diffenable = false
|
28
|
+
end
|
29
|
+
end
|
30
|
+
if diffenable == true
|
31
|
+
gitoutput = ctx.node.output.new
|
32
|
+
diff = gitoutput.get_diff ctx.node, ctx.node.group, ctx.commitref, nil
|
33
|
+
unless diff == "no diffs"
|
34
|
+
title = "#{ctx.node.name} #{ctx.node.group} #{ctx.node.model.class.name.to_s.downcase}"
|
23
35
|
log "Posting diff as snippet to #{cfg.channel}"
|
24
36
|
client.files_upload(channels: cfg.channel, as_user: true,
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
)
|
30
|
-
log "Finished"
|
37
|
+
content: diff[:patch].lines.to_a[4..-1].join,
|
38
|
+
filetype: "diff",
|
39
|
+
title: title,
|
40
|
+
filename: "change")
|
31
41
|
end
|
32
42
|
end
|
43
|
+
# message custom formatted - optional
|
44
|
+
if cfg.has_key?('message') == true
|
45
|
+
log cfg.message
|
46
|
+
msg = cfg.message % { :node => ctx.node.name.to_s, :group => ctx.node.group.to_s, :commitref => ctx.commitref, :model => ctx.node.model.class.name.to_s.downcase }
|
47
|
+
log msg
|
48
|
+
log "Posting message to #{cfg.channel}"
|
49
|
+
client.chat_postMessage(channel: cfg.channel, text: msg, as_user: true)
|
50
|
+
end
|
51
|
+
log "Finished"
|
33
52
|
end
|
34
53
|
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'xmpp4r'
|
2
|
+
require 'xmpp4r/muc/helper/simplemucclient'
|
3
|
+
|
4
|
+
class XMPPDiff < Oxidized::Hook
|
5
|
+
def validate_cfg!
|
6
|
+
raise KeyError, 'hook.jid is required' unless cfg.has_key?('jid')
|
7
|
+
raise KeyError, 'hook.password is required' unless cfg.has_key?('password')
|
8
|
+
raise KeyError, 'hook.channel is required' unless cfg.has_key?('channel')
|
9
|
+
raise KeyError, 'hook.nick is required' unless cfg.has_key?('nick')
|
10
|
+
end
|
11
|
+
|
12
|
+
def run_hook(ctx)
|
13
|
+
return unless ctx.node
|
14
|
+
return unless ctx.event.to_s == "post_store"
|
15
|
+
begin
|
16
|
+
Timeout.timeout(15) do
|
17
|
+
gitoutput = ctx.node.output.new
|
18
|
+
diff = gitoutput.get_diff ctx.node, ctx.node.group, ctx.commitref, nil
|
19
|
+
|
20
|
+
interesting = diff[:patch].lines.to_a[4..-1].any? do |line|
|
21
|
+
["+", "-"].include?(line[0]) and not ["#", "!"].include?(line[1])
|
22
|
+
end
|
23
|
+
interesting &&= diff[:patch].lines.to_a[5..-1].any? { |line| line[0] == '-' }
|
24
|
+
interesting &&= diff[:patch].lines.to_a[5..-1].any? { |line| line[0] == '+' }
|
25
|
+
|
26
|
+
if interesting
|
27
|
+
log "Connecting to XMPP"
|
28
|
+
client = Jabber::Client.new(Jabber::JID.new(cfg.jid))
|
29
|
+
client.connect
|
30
|
+
sleep 1
|
31
|
+
client.auth(cfg.password)
|
32
|
+
sleep 1
|
33
|
+
|
34
|
+
log "Connected"
|
35
|
+
|
36
|
+
m = Jabber::MUC::SimpleMUCClient.new(client)
|
37
|
+
m.join(cfg.channel + "/" + cfg.nick)
|
38
|
+
|
39
|
+
log "Joined"
|
40
|
+
|
41
|
+
title = "#{ctx.node.name} #{ctx.node.group} #{ctx.node.model.class.name.to_s.downcase}"
|
42
|
+
log "Posting diff as snippet to #{cfg.channel}"
|
43
|
+
|
44
|
+
m.say(title + "\n\n" + diff[:patch].lines.to_a[4..-1].join)
|
45
|
+
|
46
|
+
sleep 1
|
47
|
+
|
48
|
+
client.close
|
49
|
+
|
50
|
+
log "Finished"
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
54
|
+
rescue Timeout::Error
|
55
|
+
log "timed out"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/oxidized/input/cli.rb
CHANGED
@@ -32,26 +32,25 @@ module Oxidized
|
|
32
32
|
@pre_logout.each { |command, block| block ? block.call : (cmd command, nil) }
|
33
33
|
end
|
34
34
|
|
35
|
-
def post_login _post_login=nil, &block
|
35
|
+
def post_login _post_login = nil, &block
|
36
36
|
unless @exec
|
37
37
|
@post_login << [_post_login, block]
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
|
-
def pre_logout _pre_logout=nil, &block
|
41
|
+
def pre_logout _pre_logout = nil, &block
|
42
42
|
unless @exec
|
43
|
-
@pre_logout <<
|
43
|
+
@pre_logout << [_pre_logout, block]
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
-
def username re
|
47
|
+
def username re = /^(Username|login)/
|
48
48
|
@username or @username = re
|
49
49
|
end
|
50
50
|
|
51
|
-
def password re
|
51
|
+
def password re = /^Password/
|
52
52
|
@password or @password = re
|
53
53
|
end
|
54
|
-
|
55
54
|
end
|
56
55
|
end
|
57
56
|
end
|
data/lib/oxidized/input/ftp.rb
CHANGED
@@ -6,20 +6,22 @@ module Oxidized
|
|
6
6
|
class FTP < Input
|
7
7
|
RescueFail = {
|
8
8
|
:debug => [
|
9
|
-
#Net::SSH::Disconnect,
|
9
|
+
# Net::SSH::Disconnect,
|
10
10
|
],
|
11
11
|
:warn => [
|
12
|
-
#RuntimeError,
|
13
|
-
#Net::SSH::AuthenticationFailed,
|
12
|
+
# RuntimeError,
|
13
|
+
# Net::SSH::AuthenticationFailed,
|
14
14
|
],
|
15
15
|
}
|
16
16
|
include Input::CLI
|
17
17
|
|
18
18
|
def connect node
|
19
|
-
@node
|
19
|
+
@node = node
|
20
20
|
@node.model.cfg['ftp'].each { |cb| instance_exec(&cb) }
|
21
21
|
@log = File.open(Oxidized::Config::Log + "/#{@node.ip}-ftp", 'w') if Oxidized.config.input.debug?
|
22
|
-
@ftp = Net::FTP.new
|
22
|
+
@ftp = Net::FTP.new(@node.ip)
|
23
|
+
@ftp.passive = Oxidized.config.input.ftp.passive
|
24
|
+
@ftp.login @node.auth[:username], @node.auth[:password]
|
23
25
|
connected?
|
24
26
|
end
|
25
27
|
|
@@ -45,10 +47,9 @@ module Oxidized
|
|
45
47
|
|
46
48
|
def disconnect
|
47
49
|
@ftp.close
|
48
|
-
#rescue Errno::ECONNRESET, IOError
|
50
|
+
# rescue Errno::ECONNRESET, IOError
|
49
51
|
ensure
|
50
52
|
@log.close if Oxidized.config.input.debug?
|
51
53
|
end
|
52
|
-
|
53
54
|
end
|
54
55
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Oxidized
|
2
|
+
require_relative "cli"
|
3
|
+
|
4
|
+
begin
|
5
|
+
require "mechanize"
|
6
|
+
rescue LoadError
|
7
|
+
raise OxidizedError, "mechanize not found: sudo gem install mechanize"
|
8
|
+
end
|
9
|
+
|
10
|
+
class HTTP < Input
|
11
|
+
include Input::CLI
|
12
|
+
|
13
|
+
def connect node
|
14
|
+
@node = node
|
15
|
+
@m = Mechanize.new
|
16
|
+
@log = File.open(Oxidized::Config::Log + "/#{@node.ip}-http", "w") if Oxidized.config.input.debug?
|
17
|
+
|
18
|
+
@node.model.cfg["http"].each { |cb| instance_exec(&cb) }
|
19
|
+
|
20
|
+
url = URI::HTTP.build host: @node.ip, path: @main_page
|
21
|
+
@m_page = @m.get(url.to_s)
|
22
|
+
login
|
23
|
+
end
|
24
|
+
|
25
|
+
def cmd callback
|
26
|
+
instance_exec(&callback)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def log str
|
32
|
+
@log.write(str) if @log
|
33
|
+
end
|
34
|
+
|
35
|
+
def disconnect
|
36
|
+
@log.close if Oxidized.config.input.debug?
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/oxidized/input/ssh.rb
CHANGED
@@ -24,20 +24,24 @@ module Oxidized
|
|
24
24
|
secure = Oxidized.config.input.ssh.secure
|
25
25
|
@log = File.open(Oxidized::Config::Log + "/#{@node.ip}-ssh", 'w') if Oxidized.config.input.debug?
|
26
26
|
port = vars(:ssh_port) || 22
|
27
|
-
|
27
|
+
|
28
28
|
ssh_opts = {
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
29
|
+
port: port.to_i,
|
30
|
+
paranoid: secure,
|
31
|
+
keepalive: true,
|
32
|
+
password: @node.auth[:password], :timeout => Oxidized.config.timeout,
|
33
|
+
number_of_password_prompts: 0,
|
34
|
+
}
|
35
|
+
|
36
|
+
auth_methods = vars(:auth_methods) || %w(none publickey password)
|
37
|
+
ssh_opts[:auth_methods] = auth_methods
|
38
|
+
Oxidized.logger.debug "AUTH METHODS::#{auth_methods}"
|
35
39
|
|
36
40
|
if proxy_host = vars(:ssh_proxy)
|
37
41
|
proxy_command = "ssh "
|
38
42
|
proxy_command += "-o StrictHostKeyChecking=no " unless secure
|
39
43
|
proxy_command += "#{proxy_host} -W %h:%p"
|
40
|
-
proxy =
|
44
|
+
proxy = Net::SSH::Proxy::Command.new(proxy_command)
|
41
45
|
ssh_opts[:proxy] = proxy
|
42
46
|
end
|
43
47
|
|
@@ -52,7 +56,7 @@ module Oxidized
|
|
52
56
|
begin
|
53
57
|
login
|
54
58
|
rescue Timeout::Error
|
55
|
-
raise PromptUndetect, [
|
59
|
+
raise PromptUndetect, [@output, 'not matching configured prompt', @node.prompt].join(' ')
|
56
60
|
end
|
57
61
|
end
|
58
62
|
connected?
|
@@ -62,7 +66,7 @@ module Oxidized
|
|
62
66
|
@ssh and not @ssh.closed?
|
63
67
|
end
|
64
68
|
|
65
|
-
def cmd cmd, expect=node.prompt
|
69
|
+
def cmd cmd, expect = node.prompt
|
66
70
|
Oxidized.logger.debug "lib/oxidized/input/ssh.rb #{cmd} @ #{node.name} with expect: #{expect.inspect}"
|
67
71
|
if @exec
|
68
72
|
@ssh.exec! cmd
|
@@ -100,7 +104,7 @@ module Oxidized
|
|
100
104
|
ch.on_data do |_ch, data|
|
101
105
|
if Oxidized.config.input.debug?
|
102
106
|
@log.print data
|
103
|
-
@log.
|
107
|
+
@log.flush
|
104
108
|
end
|
105
109
|
@output << data
|
106
110
|
@output = @node.model.expects @output
|
@@ -117,19 +121,18 @@ module Oxidized
|
|
117
121
|
# some models have SSH auth or terminal auth based on version of code
|
118
122
|
# if SSH is configured for terminal auth, we'll still try to detect prompt
|
119
123
|
def login
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
expect @node.prompt
|
124
|
+
match_re = [@node.prompt]
|
125
|
+
match_re << @username if @username
|
126
|
+
match_re << @password if @password
|
127
|
+
until (match = expect(match_re)) == @node.prompt
|
128
|
+
cmd(@node.auth[:username], nil) if match == @username
|
129
|
+
cmd(@node.auth[:password], nil) if match == @password
|
130
|
+
match_re.delete match
|
128
131
|
end
|
129
132
|
end
|
130
133
|
|
131
|
-
def exec state=nil
|
132
|
-
state == nil ? @exec : (@exec=state) unless vars :ssh_no_exec
|
134
|
+
def exec state = nil
|
135
|
+
state == nil ? @exec : (@exec = state) unless vars :ssh_no_exec
|
133
136
|
end
|
134
137
|
|
135
138
|
def cmd_shell(cmd, expect_re)
|
@@ -152,6 +155,5 @@ module Oxidized
|
|
152
155
|
end
|
153
156
|
end
|
154
157
|
end
|
155
|
-
|
156
158
|
end
|
157
159
|
end
|
@@ -10,15 +10,16 @@ module Oxidized
|
|
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 = { 'Host' => @node.ip,
|
17
|
+
'Port' => port.to_i,
|
18
|
+
'Timeout' => @timeout,
|
19
|
+
'Model' => @node.model,
|
20
|
+
'Log' => @log }
|
20
21
|
|
21
|
-
@telnet
|
22
|
+
@telnet = Net::Telnet.new telnet_opts
|
22
23
|
if @node.auth[:username] and @node.auth[:username].length > 0
|
23
24
|
expect username
|
24
25
|
@telnet.puts @node.auth[:username]
|
@@ -28,7 +29,7 @@ module Oxidized
|
|
28
29
|
begin
|
29
30
|
expect @node.prompt
|
30
31
|
rescue Timeout::Error
|
31
|
-
raise PromptUndetect, [
|
32
|
+
raise PromptUndetect, ['unable to detect prompt:', @node.prompt].join(' ')
|
32
33
|
end
|
33
34
|
end
|
34
35
|
|
@@ -36,7 +37,7 @@ module Oxidized
|
|
36
37
|
@telnet and not @telnet.sock.closed?
|
37
38
|
end
|
38
39
|
|
39
|
-
def cmd cmd, expect
|
40
|
+
def cmd cmd, expect = @node.prompt
|
40
41
|
Oxidized.logger.debug "Telnet: #{cmd} @#{@node.name}"
|
41
42
|
args = { 'String' => cmd }
|
42
43
|
args.merge!({ 'Match' => expect, 'Timeout' => @timeout }) if expect
|
@@ -62,13 +63,14 @@ module Oxidized
|
|
62
63
|
disconnect_cli
|
63
64
|
@telnet.close
|
64
65
|
rescue Errno::ECONNRESET
|
66
|
+
ensure
|
67
|
+
@log.close if Oxidized.config.input.debug?
|
68
|
+
(@telnet.close rescue true) unless @telnet.sock.closed?
|
65
69
|
end
|
66
70
|
end
|
67
|
-
|
68
71
|
end
|
69
72
|
end
|
70
73
|
|
71
|
-
|
72
74
|
class Net::Telnet
|
73
75
|
## FIXME: we just need 'line = model.expects line' to handle pager
|
74
76
|
## how to do this, without redefining the whole damn thing
|
@@ -79,6 +81,7 @@ class Net::Telnet
|
|
79
81
|
waittime = @options["Waittime"]
|
80
82
|
fail_eof = @options["FailEOF"]
|
81
83
|
model = @options["Model"]
|
84
|
+
@log = @options["Log"]
|
82
85
|
|
83
86
|
if options.kind_of?(Hash)
|
84
87
|
prompt = if options.has_key?("Match")
|
@@ -86,7 +89,7 @@ class Net::Telnet
|
|
86
89
|
elsif options.has_key?("Prompt")
|
87
90
|
options["Prompt"]
|
88
91
|
elsif options.has_key?("String")
|
89
|
-
Regexp.new(
|
92
|
+
Regexp.new(Regexp.quote(options["String"]))
|
90
93
|
end
|
91
94
|
time_out = options["Timeout"] if options.has_key?("Timeout")
|
92
95
|
waittime = options["Waittime"] if options.has_key?("Waittime")
|
@@ -102,9 +105,9 @@ class Net::Telnet
|
|
102
105
|
line = ''
|
103
106
|
buf = ''
|
104
107
|
rest = ''
|
105
|
-
until
|
108
|
+
until prompt === line and not IO::select([@sock], nil, nil, waittime)
|
106
109
|
unless IO::select([@sock], nil, nil, time_out)
|
107
|
-
raise
|
110
|
+
raise Timeout::Error, "timed out while waiting for more data"
|
108
111
|
end
|
109
112
|
begin
|
110
113
|
c = @sock.readpartial(1024 * 1024)
|
@@ -114,32 +117,35 @@ class Net::Telnet
|
|
114
117
|
c = rest + c
|
115
118
|
if Integer(c.rindex(/#{IAC}#{SE}/no) || 0) <
|
116
119
|
Integer(c.rindex(/#{IAC}#{SB}/no) || 0)
|
117
|
-
buf = preprocess(c[0
|
118
|
-
rest = c[c.rindex(/#{IAC}#{SB}/no)
|
120
|
+
buf = preprocess(c[0...c.rindex(/#{IAC}#{SB}/no)])
|
121
|
+
rest = c[c.rindex(/#{IAC}#{SB}/no)..-1]
|
119
122
|
elsif pt = c.rindex(/#{IAC}[^#{IAC}#{AO}#{AYT}#{DM}#{IP}#{NOP}]?\z/no) ||
|
120
123
|
c.rindex(/\r\z/no)
|
121
|
-
buf = preprocess(c[0
|
122
|
-
rest = c[pt
|
124
|
+
buf = preprocess(c[0...pt])
|
125
|
+
rest = c[pt..-1]
|
123
126
|
else
|
124
127
|
buf = preprocess(c)
|
125
128
|
rest = ''
|
126
129
|
end
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
130
|
+
else
|
131
|
+
# Not Telnetmode.
|
132
|
+
#
|
133
|
+
# We cannot use preprocess() on this data, because that
|
134
|
+
# method makes some Telnetmode-specific assumptions.
|
135
|
+
buf = rest + c
|
136
|
+
rest = ''
|
137
|
+
unless @options["Binmode"]
|
138
|
+
if pt = buf.rindex(/\r\z/no)
|
139
|
+
buf = buf[0...pt]
|
140
|
+
rest = buf[pt..-1]
|
141
|
+
end
|
142
|
+
buf.gsub!(/#{EOL}/no, "\n")
|
143
|
+
end
|
144
|
+
end
|
145
|
+
if Oxidized.config.input.debug?
|
146
|
+
@log.print buf
|
147
|
+
@log.flush
|
141
148
|
end
|
142
|
-
@log.print(buf) if @options.has_key?("Output_log")
|
143
149
|
line += buf
|
144
150
|
line = model.expects line
|
145
151
|
line = yield line if block_given?
|