oxidized 0.28.0 → 0.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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