oxidized 0.28.0 → 0.29.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (140) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/publishdocker.yml +8 -1
  3. data/.github/workflows/ruby.yml +42 -0
  4. data/.rubocop.yml +30 -10
  5. data/.rubocop_todo.yml +95 -41
  6. data/CHANGELOG.md +139 -2
  7. data/Dockerfile +13 -9
  8. data/README.md +66 -32
  9. data/Rakefile +2 -0
  10. data/docs/Configuration.md +49 -7
  11. data/docs/Creating-Models.md +10 -4
  12. data/docs/Hooks.md +35 -6
  13. data/docs/Model-Notes/ADVA.md +12 -0
  14. data/docs/Model-Notes/Cumulus.md +7 -1
  15. data/docs/Model-Notes/IOS.md +36 -0
  16. data/docs/Model-Notes/JunOS.md +3 -3
  17. data/docs/Model-Notes/LenovoNOS.md +29 -0
  18. data/docs/Model-Notes/LinksysSRW.md +15 -0
  19. data/docs/Model-Notes/Nokia.md +3 -0
  20. data/docs/Model-Notes/OS10.md +33 -0
  21. data/docs/Model-Notes/PanOS_API.md +28 -0
  22. data/docs/Model-Notes/README.md +2 -0
  23. data/docs/Sources.md +18 -0
  24. data/docs/Supported-OS-Types.md +51 -6
  25. data/docs/Troubleshooting.md +1 -1
  26. data/extra/gitdiff-msteams.sh +91 -0
  27. data/extra/nagios_check_failing_nodes.rb +6 -9
  28. data/extra/oxidized-report-git-commits +71 -14
  29. data/extra/oxidized.init +2 -5
  30. data/extra/oxidized.logrotate +1 -0
  31. data/extra/oxidized.runit +4 -1
  32. data/extra/oxidized.service +5 -8
  33. data/extra/rest_client.rb +1 -1
  34. data/extra/syslog.rb +2 -2
  35. data/lib/oxidized/cli.rb +1 -1
  36. data/lib/oxidized/config/vars.rb +5 -2
  37. data/lib/oxidized/config.rb +6 -3
  38. data/lib/oxidized/core.rb +1 -1
  39. data/lib/oxidized/hook/exec.rb +6 -6
  40. data/lib/oxidized/hook/githubrepo.rb +42 -11
  41. data/lib/oxidized/hook/slackdiff.rb +2 -2
  42. data/lib/oxidized/hook/xmppdiff.rb +45 -25
  43. data/lib/oxidized/hook.rb +4 -8
  44. data/lib/oxidized/input/exec.rb +1 -1
  45. data/lib/oxidized/input/input.rb +1 -0
  46. data/lib/oxidized/input/ssh.rb +23 -20
  47. data/lib/oxidized/input/telnet.rb +52 -44
  48. data/lib/oxidized/job.rb +1 -0
  49. data/lib/oxidized/jobs.rb +11 -6
  50. data/lib/oxidized/manager.rb +1 -0
  51. data/lib/oxidized/model/acmepacket.rb +38 -0
  52. data/lib/oxidized/model/adtran.rb +5 -3
  53. data/lib/oxidized/model/adva.rb +66 -0
  54. data/lib/oxidized/model/airfiber.rb +1 -1
  55. data/lib/oxidized/model/aoscx.rb +96 -0
  56. data/lib/oxidized/model/aosw.rb +1 -1
  57. data/lib/oxidized/model/asa.rb +2 -0
  58. data/lib/oxidized/model/awplus.rb +1 -1
  59. data/lib/oxidized/model/bdcom.rb +49 -0
  60. data/lib/oxidized/model/cambiumepmp.rb +17 -0
  61. data/lib/oxidized/model/casa.rb +4 -1
  62. data/lib/oxidized/model/ciscoce.rb +12 -0
  63. data/lib/oxidized/model/ciscosmb.rb +2 -0
  64. data/lib/oxidized/model/comware.rb +16 -1
  65. data/lib/oxidized/model/cumulus.rb +58 -44
  66. data/lib/oxidized/model/dellx.rb +1 -3
  67. data/lib/oxidized/model/dlink.rb +2 -1
  68. data/lib/oxidized/model/edgecos.rb +22 -2
  69. data/lib/oxidized/model/edgeswitch.rb +4 -4
  70. data/lib/oxidized/model/eltex.rb +48 -0
  71. data/lib/oxidized/model/enterasys.rb +18 -3
  72. data/lib/oxidized/model/enterasys800.rb +29 -0
  73. data/lib/oxidized/model/eos.rb +2 -1
  74. data/lib/oxidized/model/fabricos.rb +1 -1
  75. data/lib/oxidized/model/fastiron.rb +3 -2
  76. data/lib/oxidized/model/fortios.rb +24 -11
  77. data/lib/oxidized/model/fortiwlc.rb +24 -0
  78. data/lib/oxidized/model/gaiaos.rb +40 -3
  79. data/lib/oxidized/model/h3c.rb +40 -0
  80. data/lib/oxidized/model/hatteras.rb +2 -2
  81. data/lib/oxidized/model/hios.rb +38 -0
  82. data/lib/oxidized/model/hpebladesystem.rb +1 -1
  83. data/lib/oxidized/model/ios.rb +13 -10
  84. data/lib/oxidized/model/iosxe.rb +1 -1
  85. data/lib/oxidized/model/ironware.rb +8 -4
  86. data/lib/oxidized/model/junos.rb +5 -1
  87. data/lib/oxidized/model/lancom.rb +23 -0
  88. data/lib/oxidized/model/lenovonos.rb +82 -0
  89. data/lib/oxidized/model/linksyssrw.rb +71 -0
  90. data/lib/oxidized/model/mlnxos.rb +2 -0
  91. data/lib/oxidized/model/model.rb +29 -3
  92. data/lib/oxidized/model/necix.rb +30 -0
  93. data/lib/oxidized/model/netgear.rb +5 -2
  94. data/lib/oxidized/model/netscaler.rb +38 -1
  95. data/lib/oxidized/model/nodegrid.rb +23 -0
  96. data/lib/oxidized/model/nxos.rb +3 -2
  97. data/lib/oxidized/model/openbsd.rb +9 -0
  98. data/lib/oxidized/model/opengear.rb +1 -1
  99. data/lib/oxidized/model/opnsense.rb +12 -4
  100. data/lib/oxidized/model/panos_api.rb +71 -0
  101. data/lib/oxidized/model/pfsense.rb +12 -7
  102. data/lib/oxidized/model/powerconnect.rb +1 -3
  103. data/lib/oxidized/model/procurve.rb +2 -2
  104. data/lib/oxidized/model/purityos.rb +8 -1
  105. data/lib/oxidized/model/quantaos.rb +1 -5
  106. data/lib/oxidized/model/routeros.rb +15 -2
  107. data/lib/oxidized/model/slxos.rb +1 -0
  108. data/lib/oxidized/model/smartcs.rb +40 -0
  109. data/lib/oxidized/model/sonicos.rb +9 -1
  110. data/lib/oxidized/model/srosmd.rb +97 -0
  111. data/lib/oxidized/model/stoneos.rb +6 -2
  112. data/lib/oxidized/model/supermicro.rb +1 -1
  113. data/lib/oxidized/model/swos.rb +9 -0
  114. data/lib/oxidized/model/timos.rb +1 -1
  115. data/lib/oxidized/model/tmos.rb +2 -1
  116. data/lib/oxidized/model/tplink.rb +2 -0
  117. data/lib/oxidized/model/trango.rb +11 -11
  118. data/lib/oxidized/model/truenas.rb +20 -0
  119. data/lib/oxidized/model/vrp.rb +1 -1
  120. data/lib/oxidized/model/xos.rb +4 -3
  121. data/lib/oxidized/model/yamaha.rb +57 -0
  122. data/lib/oxidized/model/zteolt.rb +52 -0
  123. data/lib/oxidized/model/zy1308.rb +11 -0
  124. data/lib/oxidized/node/stats.rb +1 -0
  125. data/lib/oxidized/node.rb +16 -11
  126. data/lib/oxidized/nodes.rb +7 -6
  127. data/lib/oxidized/output/file.rb +2 -1
  128. data/lib/oxidized/output/git.rb +4 -3
  129. data/lib/oxidized/output/gitcrypt.rb +5 -8
  130. data/lib/oxidized/output/http.rb +2 -0
  131. data/lib/oxidized/source/csv.rb +1 -0
  132. data/lib/oxidized/source/http.rb +4 -0
  133. data/lib/oxidized/source/source.rb +7 -2
  134. data/lib/oxidized/source/sql.rb +15 -5
  135. data/lib/oxidized/string.rb +9 -3
  136. data/lib/oxidized/version.rb +2 -2
  137. data/lib/oxidized/worker.rb +5 -5
  138. data/oxidized.gemspec +22 -16
  139. metadata +116 -29
  140. data/.travis.yml +0 -10
@@ -4,18 +4,32 @@ class GithubRepo < Oxidized::Hook
4
4
  end
5
5
 
6
6
  def run_hook(ctx)
7
- repo = Rugged::Repository.new(ctx.node.repo)
7
+ repo = Rugged::Repository.new(ctx.node.repo)
8
+ creds = credentials(ctx.node)
9
+ url = remote_repo(ctx.node)
10
+
11
+ if url.nil? || url.empty?
12
+ log "No repository defined for #{ctx.node.group}/#{ctx.node.name}", :debug
13
+ return
14
+ end
15
+
8
16
  log "Pushing local repository(#{repo.path})..."
9
- remote = repo.remotes['origin'] || repo.remotes.create('origin', remote_repo(ctx.node))
10
- log "to remote: #{remote.url}"
17
+ log "to remote: #{url}"
11
18
 
12
- fetch_and_merge_remote(repo)
19
+ if repo.remotes['origin'].nil?
20
+ repo.remotes.create('origin', url)
21
+ elsif repo.remotes['origin'].url != url
22
+ repo.remotes.set_url('origin', url)
23
+ end
24
+ remote = repo.remotes['origin']
25
+
26
+ fetch_and_merge_remote(repo, creds)
13
27
 
14
- remote.push([repo.head.name], credentials: credentials)
28
+ remote.push([repo.head.name], credentials: creds)
15
29
  end
16
30
 
17
- def fetch_and_merge_remote(repo)
18
- result = repo.fetch('origin', [repo.head.name], credentials: credentials)
31
+ def fetch_and_merge_remote(repo, creds)
32
+ result = repo.fetch('origin', [repo.head.name], credentials: creds)
19
33
  log result.inspect, :debug
20
34
 
21
35
  unless result[:total_deltas].positive?
@@ -43,15 +57,20 @@ class GithubRepo < Oxidized::Hook
43
57
 
44
58
  private
45
59
 
46
- def credentials
60
+ def credentials(node)
47
61
  Proc.new do |_url, username_from_url, _allowed_types| # rubocop:disable Style/Proc
48
62
  git_user = cfg.has_key?('username') ? cfg.username : (username_from_url || 'git')
49
63
  if cfg.has_key?('password')
50
64
  log "Authenticating using username and password as '#{git_user}'", :debug
51
65
  Rugged::Credentials::UserPassword.new(username: git_user, password: cfg.password)
52
- elsif cfg.has_key?('publickey') && cfg.has_key?('privatekey')
66
+ elsif cfg.has_key?('privatekey')
67
+ pubkey = cfg.has_key?('publickey') ? cfg.publickey : nil
53
68
  log "Authenticating using ssh keys as '#{git_user}'", :debug
54
- Rugged::Credentials::SshKey.new(username: git_user, publickey: File.expand_path(cfg.publickey), privatekey: File.expand_path(cfg.privatekey), passphrase: ENV["OXIDIZED_SSH_PASSPHRASE"])
69
+ rugged_sshkey(git_user: git_user, privkey: cfg.privatekey, pubkey: pubkey)
70
+ elsif cfg.has_key?('remote_repo') && cfg.remote_repo.has_key?(node.group) && cfg.remote_repo[node.group].has_key?('privatekey')
71
+ pubkey = cfg.remote_repo[node.group].has_key?('publickey') ? cfg.remote_repo[node.group].publickey : nil
72
+ log "Authenticating using ssh keys as '#{git_user}' for '#{node.group}/#{node.name}'", :debug
73
+ rugged_sshkey(git_user: git_user, privkey: cfg.remote_repo[node.group].privatekey, pubkey: pubkey)
55
74
  else
56
75
  log "Authenticating using ssh agent as '#{git_user}'", :debug
57
76
  Rugged::Credentials::SshKeyFromAgent.new(username: git_user)
@@ -59,11 +78,23 @@ class GithubRepo < Oxidized::Hook
59
78
  end
60
79
  end
61
80
 
81
+ def rugged_sshkey(args = {})
82
+ git_user = args[:git_user]
83
+ privkey = args[:privkey]
84
+ pubkey = args[:pubkey] || (privkey + '.pub')
85
+ Rugged::Credentials::SshKey.new(username: git_user,
86
+ publickey: File.expand_path(pubkey),
87
+ privatekey: File.expand_path(privkey),
88
+ passphrase: ENV.fetch("OXIDIZED_SSH_PASSPHRASE", nil))
89
+ end
90
+
62
91
  def remote_repo(node)
63
92
  if node.group.nil? || cfg.remote_repo.is_a?(String)
64
93
  cfg.remote_repo
65
- else
94
+ elsif cfg.remote_repo[node.group].is_a?(String)
66
95
  cfg.remote_repo[node.group]
96
+ elsif cfg.remote_repo[node.group].url.is_a?(String)
97
+ cfg.remote_repo[node.group].url
67
98
  end
68
99
  end
69
100
  end
@@ -14,11 +14,11 @@ class SlackDiff < Oxidized::Hook
14
14
  return unless ctx.event.to_s == "post_store"
15
15
 
16
16
  log "Connecting to slack"
17
- Slack.configure do |config|
17
+ Slack::Web::Client.configure do |config|
18
18
  config.token = cfg.token
19
19
  config.proxy = cfg.proxy if cfg.has_key?('proxy')
20
20
  end
21
- client = Slack::Client.new
21
+ client = Slack::Web::Client.new
22
22
  client.auth_test
23
23
  log "Connected"
24
24
  if cfg.has_key?("diff") ? cfg.diff : true
@@ -2,6 +2,44 @@ require 'xmpp4r'
2
2
  require 'xmpp4r/muc/helper/simplemucclient'
3
3
 
4
4
  class XMPPDiff < Oxidized::Hook
5
+ def connect
6
+ @client = Jabber::Client.new(Jabber::JID.new(cfg.jid))
7
+
8
+ log "Connecting to XMPP"
9
+ begin
10
+ Timeout.timeout(15) do
11
+ begin
12
+ @client.connect
13
+ rescue StandardError => e
14
+ log "Failed to connect to XMPP: #{e}"
15
+ end
16
+ sleep 1
17
+
18
+ log "Authenticating to XMPP"
19
+ @client.auth(cfg.password)
20
+ sleep 1
21
+
22
+ log "Connected to XMPP"
23
+
24
+ @muc = Jabber::MUC::SimpleMUCClient.new(@client)
25
+ @muc.join(cfg.channel + "/" + cfg.nick)
26
+
27
+ log "Joined #{cfg.channel}"
28
+ end
29
+ rescue Timeout::Error
30
+ log "timed out"
31
+ @client = nil
32
+ @muc = nil
33
+ end
34
+
35
+ @client.on_exception do
36
+ log "XMPP connection aborted, reconnecting"
37
+ @client = nil
38
+ @muc = nil
39
+ connect
40
+ end
41
+ end
42
+
5
43
  def validate_cfg!
6
44
  raise KeyError, 'hook.jid is required' unless cfg.has_key?('jid')
7
45
  raise KeyError, 'hook.password is required' unless cfg.has_key?('password')
@@ -21,35 +59,17 @@ class XMPPDiff < Oxidized::Hook
21
59
  interesting = diff[:patch].lines.to_a[4..-1].any? do |line|
22
60
  ["+", "-"].include?(line[0]) && (not ["#", "!"].include?(line[1]))
23
61
  end
24
- interesting &&= diff[:patch].lines.to_a[5..-1].any? { |line| line[0] == '-' }
25
- interesting &&= diff[:patch].lines.to_a[5..-1].any? { |line| line[0] == '+' }
26
62
 
27
63
  if interesting
28
- log "Connecting to XMPP"
29
- client = Jabber::Client.new(Jabber::JID.new(cfg.jid))
30
- client.connect
31
- sleep 1
32
- client.auth(cfg.password)
33
- sleep 1
34
-
35
- log "Connected"
36
-
37
- m = Jabber::MUC::SimpleMUCClient.new(client)
38
- m.join(cfg.channel + "/" + cfg.nick)
39
-
40
- log "Joined"
41
-
42
- title = "#{ctx.node.name} #{ctx.node.group} #{ctx.node.model.class.name.to_s.downcase}"
43
- log "Posting diff as snippet to #{cfg.channel}"
44
-
45
- m.say(title + "\n\n" + diff[:patch].lines.to_a[4..-1].join)
46
-
47
- sleep 1
48
-
49
- client.close
64
+ connect if @muc.nil?
50
65
 
51
- log "Finished"
66
+ # Maybe connecting failed, so only proceed if we actually joined the MUC
67
+ unless @muc.nil?
68
+ title = "#{ctx.node.name} #{ctx.node.group} #{ctx.node.model.class.name.to_s.downcase}"
69
+ log "Posting diff as snippet to #{cfg.channel}"
52
70
 
71
+ @muc.say(title + "\n\n" + diff[:patch].lines.to_a[4..-1].join)
72
+ end
53
73
  end
54
74
  end
55
75
  rescue Timeout::Error
data/lib/oxidized/hook.rb CHANGED
@@ -55,12 +55,10 @@ module Oxidized
55
55
  ctx.event = event
56
56
 
57
57
  @registered_hooks[event].each do |r_hook|
58
- begin
59
- r_hook.hook.run_hook ctx
60
- rescue StandardError => e
61
- Oxidized.logger.error "Hook #{r_hook.name} (#{r_hook.hook}) failed " \
62
- "(#{e.inspect}) for event #{event.inspect}"
63
- end
58
+ r_hook.hook.run_hook ctx
59
+ rescue StandardError => e
60
+ Oxidized.logger.error "Hook #{r_hook.name} (#{r_hook.hook}) failed " \
61
+ "(#{e.inspect}) for event #{event.inspect}"
64
62
  end
65
63
  end
66
64
  end
@@ -69,8 +67,6 @@ module Oxidized
69
67
  class Hook
70
68
  attr_reader :cfg
71
69
 
72
- def initialize; end
73
-
74
70
  def cfg=(cfg)
75
71
  @cfg = cfg
76
72
  validate_cfg! if respond_to? :validate_cfg!
@@ -6,8 +6,8 @@ module Oxidized
6
6
 
7
7
  def connect(node)
8
8
  @node = node
9
- @node.model.cfg["exec"].each { |cb| instance_exec(&cb) }
10
9
  @log = File.open(Oxidized::Config::Log + "/#{@node.ip}-exec", "w") if Oxidized.config.input.debug?
10
+ @node.model.cfg["exec"].each { |cb| instance_exec(&cb) }
11
11
  end
12
12
 
13
13
  def cmd(cmd_str)
@@ -1,5 +1,6 @@
1
1
  module Oxidized
2
2
  class PromptUndetect < OxidizedError; end
3
+
3
4
  class Input
4
5
  include Oxidized::Config::Vars
5
6
 
@@ -122,34 +122,27 @@ module Oxidized
122
122
  def make_ssh_opts
123
123
  secure = Oxidized.config.input.ssh.secure?
124
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
125
+ number_of_password_prompts: 0,
126
+ keepalive: vars(:ssh_no_keepalive) ? false : true,
127
+ verify_host_key: secure ? :always : :never,
128
+ append_all_supported_algorithms: true,
129
+ password: @node.auth[:password],
130
+ timeout: Oxidized.config.timeout,
131
+ port: (vars(:ssh_port) || 22).to_i,
132
+ forward_agent: false
131
133
  }
132
134
 
133
135
  auth_methods = vars(:auth_methods) || %w[none publickey password]
134
136
  ssh_opts[:auth_methods] = auth_methods
135
137
  Oxidized.logger.debug "AUTH METHODS::#{auth_methods}"
136
138
 
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
139
+ ssh_opts[:proxy] = make_ssh_proxy_command(vars(:ssh_proxy), vars(:ssh_proxy_port), secure) if vars(:ssh_proxy)
147
140
 
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)
141
+ ssh_opts[:keys] = [vars(:ssh_keys)].flatten if vars(:ssh_keys)
142
+ ssh_opts[:kex] = vars(:ssh_kex).split(/,\s*/) if vars(:ssh_kex)
150
143
  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)
144
+ ssh_opts[:host_key] = vars(:ssh_host_key).split(/,\s*/) if vars(:ssh_host_key)
145
+ ssh_opts[:hmac] = vars(:ssh_hmac).split(/,\s*/) if vars(:ssh_hmac)
153
146
 
154
147
  if Oxidized.config.input.debug?
155
148
  ssh_opts[:logger] = Oxidized.logger
@@ -158,5 +151,15 @@ module Oxidized
158
151
 
159
152
  ssh_opts
160
153
  end
154
+
155
+ def make_ssh_proxy_command(proxy_host, proxy_port, secure)
156
+ return nil unless !proxy_host.nil? && !proxy_host.empty?
157
+
158
+ proxy_command = "ssh "
159
+ proxy_command += "-o StrictHostKeyChecking=no " unless secure
160
+ proxy_command += "-p #{proxy_port} " if proxy_port
161
+ proxy_command += "#{proxy_host} -W [%h]:%p"
162
+ Net::SSH::Proxy::Command.new(proxy_command)
163
+ end
161
164
  end
162
165
  end
@@ -35,13 +35,15 @@ module Oxidized
35
35
  end
36
36
 
37
37
  def cmd(cmd_str, expect = @node.prompt)
38
+ Oxidized.logger.debug "Telnet: #{cmd_str} @#{@node.name}"
38
39
  return send(cmd_str + "\r\n") unless expect
39
40
 
40
- Oxidized.logger.debug "Telnet: #{cmd_str} @#{@node.name}"
41
- args = { 'String' => cmd_str,
42
- 'Match' => expect,
43
- 'Timeout' => @timeout }
44
- @telnet.cmd args
41
+ # create a string to be passed to oxidized_expect and modified _there_
42
+ # default to a single space so it shouldn't be coerced to nil by any models.
43
+ out = String(' ')
44
+ @telnet.puts(cmd_str)
45
+ @telnet.oxidized_expect(timeout: @timeout, expect: expect, out: out)
46
+ out
45
47
  end
46
48
 
47
49
  def send(data)
@@ -69,46 +71,52 @@ module Oxidized
69
71
  end
70
72
  end
71
73
 
72
- class Net::Telnet
73
- ## how to do this, without redefining the whole damn thing
74
- ## FIXME: we also need output (not sure I'm going to support this)
75
- attr_reader :output
76
- def oxidized_expect(options)
77
- model = @options["Model"]
78
- @log = @options["Log"]
79
-
80
- expects = [options[:expect]].flatten
81
- time_out = options[:timeout] || @options["Timeout"] || Oxidized.config.timeout?
82
-
83
- Timeout.timeout(time_out) do
84
- line = ""
85
- rest = ""
86
- buf = ""
87
- loop do
88
- c = @sock.readpartial(1024 * 1024)
89
- @output = c
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
74
+ module Net
75
+ class Telnet
76
+ ## how to do this, without redefining the whole damn thing
77
+ ## FIXME: we also need output (not sure I'm going to support this)
78
+ attr_reader :output
79
+
80
+ def oxidized_expect(options) ## rubocop:disable Metrics/PerceivedComplexity
81
+ model = @options["Model"]
82
+ @log = @options["Log"]
83
+
84
+ expects = [options[:expect]].flatten
85
+ time_out = options[:timeout] || @options["Timeout"] || Oxidized.config.timeout?
86
+
87
+ Timeout.timeout(time_out) do
88
+ line = ""
89
+ rest = ""
90
+ buf = ""
91
+ loop do
92
+ c = @sock.readpartial(1024 * 1024)
93
+ @output = c
94
+ c = rest + c
95
+
96
+ if Integer(c.rindex(/#{IAC}#{SE}/no) || 0) <
97
+ Integer(c.rindex(/#{IAC}#{SB}/no) || 0)
98
+ buf = preprocess(c[0...c.rindex(/#{IAC}#{SB}/no)])
99
+ rest = c[c.rindex(/#{IAC}#{SB}/no)..-1]
100
+ elsif (pt = c.rindex(/#{IAC}[^#{IAC}#{AO}#{AYT}#{DM}#{IP}#{NOP}]?\z/no) ||
101
+ c.rindex(/\r\z/no))
102
+ buf = preprocess(c[0...pt])
103
+ rest = c[pt..-1]
104
+ else
105
+ buf = preprocess(c)
106
+ rest = ''
107
+ end
108
+ if Oxidized.config.input.debug?
109
+ @log.print buf
110
+ @log.flush
111
+ end
112
+ line += buf
113
+ line = model.expects line
114
+ # match is a regexp object. we need to return that for logins to work.
115
+ match = expects.find { |re| line.match re }
116
+ # stomp on the out string object if we have one. (thus we were called by cmd?)
117
+ options[:out]&.replace(line)
118
+ return match if match
107
119
  end
108
- line += buf
109
- line = model.expects line
110
- match = expects.find { |re| line.match re }
111
- return match if match
112
120
  end
113
121
  end
114
122
  end
data/lib/oxidized/job.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  module Oxidized
2
2
  class Job < Thread
3
3
  attr_reader :start, :end, :status, :time, :node, :config
4
+
4
5
  def initialize(node)
5
6
  @node = node
6
7
  @start = Time.now.utc
data/lib/oxidized/jobs.rb CHANGED
@@ -4,13 +4,14 @@ 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(max, interval, nodes)
8
- @max = max
7
+ def initialize(max, use_max_threads, interval, nodes)
8
+ @max = max
9
+ @use_max_threads = use_max_threads
9
10
  # Set interval to 1 if interval is 0 (=disabled) so we don't break
10
11
  # the 'ceil' function
11
- @interval = interval.zero? ? 1 : interval
12
- @nodes = nodes
13
- @last = Time.now.utc
12
+ @interval = interval.zero? ? 1 : interval
13
+ @nodes = nodes
14
+ @last = Time.now.utc
14
15
  @durations = Array.new @nodes.size, AVERAGE_DURATION
15
16
  duration AVERAGE_DURATION
16
17
  super()
@@ -33,7 +34,11 @@ module Oxidized
33
34
  end
34
35
 
35
36
  def new_count
36
- @want = ((@nodes.size * @duration) / @interval).ceil
37
+ @want = if @use_max_threads
38
+ @max
39
+ else
40
+ ((@nodes.size * @duration) / @interval).ceil
41
+ end
37
42
  @want = 1 if @want < 1
38
43
  @want = @nodes.size if @want > @nodes.size
39
44
  @want = @max if @want > @max
@@ -23,6 +23,7 @@ module Oxidized
23
23
  end
24
24
 
25
25
  attr_reader :input, :output, :source, :model, :hook
26
+
26
27
  def initialize
27
28
  @input = {}
28
29
  @output = {}
@@ -0,0 +1,38 @@
1
+ class ACMEPACKET < Oxidized::Model
2
+ # Oracle ACME Packet 3k, 4k, 6k series
3
+
4
+ prompt /^\r*([\w.@()-\/]+[#>]\s?)$/
5
+
6
+ comment '! '
7
+
8
+ cmd :all do |cfg, cmdstring|
9
+ new_cfg = comment "COMMAND: #{cmdstring}\n"
10
+ new_cfg << cfg.cut_both
11
+ end
12
+
13
+ cmd 'show version' do |cfg|
14
+ comment cfg
15
+ end
16
+
17
+ cmd 'show running-config' do |cfg|
18
+ cfg
19
+ end
20
+
21
+ cfg :telnet do
22
+ password /^Password:/i
23
+ end
24
+
25
+ cfg :telnet, :ssh do
26
+ # preferred way to handle additional passwords
27
+ post_login do
28
+ if vars(:enable) == true
29
+ cmd "enable"
30
+ elsif vars(:enable)
31
+ cmd "enable", /^[pP]assword:/
32
+ cmd vars(:enable)
33
+ end
34
+ end
35
+ pre_logout 'exit'
36
+ pre_logout 'exit'
37
+ end
38
+ end
@@ -15,9 +15,11 @@ class Adtran < Oxidized::Model
15
15
  cmd 'show running-config'
16
16
 
17
17
  cfg :ssh do
18
- post_login do
19
- send "enable\n"
20
- cmd vars(:enable)
18
+ if vars :enable
19
+ post_login do
20
+ send "enable\n"
21
+ cmd vars(:enable)
22
+ end
21
23
  end
22
24
  post_login 'terminal length 0'
23
25
  pre_logout 'exit'
@@ -0,0 +1,66 @@
1
+ # Oxidized model for ADVA devices
2
+ #
3
+ # IMPORTANT: To get this working, cli-paging must be disabled
4
+ # for the user that is used to fetch the configuration.
5
+
6
+ class ADVA < Oxidized::Model
7
+ prompt /\w+\-+[#>]\s?$/
8
+ comment '# '
9
+
10
+ cmd :secret do |cfg|
11
+ cfg.gsub! /community "[^"]+"/, 'community "<hidden>"'
12
+ cfg
13
+ end
14
+
15
+ cmd :all do |cfg|
16
+ cfg.cut_both
17
+ end
18
+
19
+ cmd 'show running-config current' do |cfg|
20
+ cfg.each_line.reject { |line| line.match /^Preparing configuration file.*/ }.join
21
+ end
22
+
23
+ cmd 'show system' do |cfg|
24
+ cfg = cfg.each_line.reject { |line| line.match /(up time|local time)/i }.join
25
+
26
+ cfg = "COMMAND: show system\n\n" + cfg
27
+ cfg = comment cfg
28
+ "\n\n" + cfg
29
+ end
30
+
31
+ cmd 'network-element ne-1'
32
+
33
+ cmd 'show shelf-info' do |cfg|
34
+ cfg = "COMMAND: show shelf-info\n\n" + cfg
35
+ cfg = comment cfg
36
+ "\n\n" + cfg
37
+ end
38
+
39
+ post do
40
+ ports = []
41
+ ports_output = ''
42
+
43
+ cmd 'show ports' do |cfg|
44
+ cfg.each_line do |line|
45
+ port = line.match(/\|((access|network)[^\|]+)\|/)
46
+ ports << port if port
47
+ end
48
+ end
49
+
50
+ ports.each do |port|
51
+ port_command = 'show ' + port[2] + '-port ' + port[1]
52
+
53
+ ports_output << cmd(port_command) do |cfg|
54
+ cfg = "COMMAND: " + port_command + "\n\n" + cfg
55
+ cfg = comment cfg
56
+ "\n\n" + cfg
57
+ end
58
+ end
59
+
60
+ ports_output
61
+ end
62
+
63
+ cfg :ssh do
64
+ pre_logout 'logout'
65
+ end
66
+ end
@@ -1,7 +1,7 @@
1
1
  class Airfiber < Oxidized::Model
2
2
  # Ubiquiti Airfiber (tested with Airfiber 11FX)
3
3
 
4
- prompt /^AF[\w\.]+#/
4
+ prompt /^AF[\w\.-]+#/i
5
5
 
6
6
  cmd :all do |cfg|
7
7
  cfg.cut_both