oxidized 0.35.0 → 0.37.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 (102) hide show
  1. checksums.yaml +4 -4
  2. data/.coderabbit.yaml +21 -0
  3. data/.github/workflows/publishdocker.yml +11 -9
  4. data/.github/workflows/ruby.yml +1 -3
  5. data/.rubocop.yml +16 -2
  6. data/.rubocop_todo.yml +21 -2
  7. data/CHANGELOG.md +76 -3
  8. data/README.md +2 -3
  9. data/Rakefile +1 -1
  10. data/docs/Configuration.md +40 -2
  11. data/docs/Creating-Models.md +129 -14
  12. data/docs/Docker.md +2 -1
  13. data/docs/Hooks.md +92 -67
  14. data/docs/Inputs.md +44 -12
  15. data/docs/Model-Notes/APC.md +72 -0
  16. data/docs/Model-Notes/ExaLink.md +43 -0
  17. data/docs/Model-Notes/Fortinet.md +75 -0
  18. data/docs/Model-Notes/GrandstreamHT8xx.md +8 -0
  19. data/docs/Model-Notes/IvantiConnectSecure.md +59 -0
  20. data/docs/Model-Notes/RouterOS.md +13 -0
  21. data/docs/Model-Notes/TrueNAS.md +23 -0
  22. data/docs/ModelUnitTests.md +23 -0
  23. data/docs/Outputs.md +18 -4
  24. data/docs/Release.md +7 -2
  25. data/docs/Ruby-API.md +86 -5
  26. data/docs/Supported-OS-Types.md +21 -9
  27. data/docs/Troubleshooting.md +1 -1
  28. data/extra/device2yaml.rb +2 -3
  29. data/extra/hooks/modelrules.rb +55 -0
  30. data/extra/hooks/modelrulesadvanced.rb +167 -0
  31. data/extra/hooks/srcipmap.rb +54 -0
  32. data/lib/oxidized/cli/support.rb +152 -0
  33. data/lib/oxidized/cli.rb +9 -0
  34. data/lib/oxidized/hook/githubrepo.rb +2 -1
  35. data/lib/oxidized/hook.rb +58 -8
  36. data/lib/oxidized/input/debugtext.rb +40 -0
  37. data/lib/oxidized/input/debugyaml.rb +82 -0
  38. data/lib/oxidized/input/exec.rb +1 -10
  39. data/lib/oxidized/input/ftp.rb +0 -17
  40. data/lib/oxidized/input/http.rb +39 -21
  41. data/lib/oxidized/input/input.rb +33 -13
  42. data/lib/oxidized/input/scp.rb +10 -64
  43. data/lib/oxidized/input/ssh.rb +36 -79
  44. data/lib/oxidized/input/sshbase.rb +102 -0
  45. data/lib/oxidized/input/telnet.rb +12 -13
  46. data/lib/oxidized/input/tftp.rb +7 -7
  47. data/lib/oxidized/model/aoscx.rb +18 -12
  48. data/lib/oxidized/model/aosw.rb +10 -11
  49. data/lib/oxidized/model/apc_aos.rb +4 -0
  50. data/lib/oxidized/model/apcaos.rb +39 -0
  51. data/lib/oxidized/model/arubainstant.rb +11 -20
  52. data/lib/oxidized/model/asa.rb +7 -7
  53. data/lib/oxidized/model/comware.rb +3 -1
  54. data/lib/oxidized/model/cumulus.rb +3 -3
  55. data/lib/oxidized/model/defacto.rb +26 -0
  56. data/lib/oxidized/model/dlinknextgen.rb +1 -0
  57. data/lib/oxidized/model/dslcommands.rb +93 -0
  58. data/lib/oxidized/model/dslsetup.rb +102 -0
  59. data/lib/oxidized/model/efos.rb +5 -5
  60. data/lib/oxidized/model/exalink.rb +36 -0
  61. data/lib/oxidized/model/fastiron.rb +2 -2
  62. data/lib/oxidized/model/firelinuxos.rb +1 -3
  63. data/lib/oxidized/model/fortigate.rb +160 -0
  64. data/lib/oxidized/model/fortios.rb +28 -69
  65. data/lib/oxidized/model/fsos.rb +1 -3
  66. data/lib/oxidized/model/grandstreamht8xx.rb +19 -0
  67. data/lib/oxidized/model/h3c.rb +1 -1
  68. data/lib/oxidized/model/ios.rb +23 -15
  69. data/lib/oxidized/model/ironware.rb +5 -3
  70. data/lib/oxidized/model/ivanti.rb +54 -0
  71. data/lib/oxidized/model/junos.rb +2 -2
  72. data/lib/oxidized/model/linuxgeneric.rb +4 -2
  73. data/lib/oxidized/model/macros.rb +60 -0
  74. data/lib/oxidized/model/mlnxos.rb +11 -7
  75. data/lib/oxidized/model/model.rb +28 -126
  76. data/lib/oxidized/model/ndms.rb +6 -0
  77. data/lib/oxidized/model/netgear.rb +5 -3
  78. data/lib/oxidized/model/nxos.rb +6 -3
  79. data/lib/oxidized/model/outputs.rb +5 -0
  80. data/lib/oxidized/model/perle.rb +14 -8
  81. data/lib/oxidized/model/routeros.rb +4 -0
  82. data/lib/oxidized/model/smartbyte.rb +48 -0
  83. data/lib/oxidized/model/tplink.rb +4 -6
  84. data/lib/oxidized/model/truenas.rb +63 -3
  85. data/lib/oxidized/model/voss.rb +3 -0
  86. data/lib/oxidized/model/vyos.rb +4 -1
  87. data/lib/oxidized/node.rb +25 -23
  88. data/lib/oxidized/nodes.rb +2 -0
  89. data/lib/oxidized/output/file.rb +7 -1
  90. data/lib/oxidized/output/git.rb +11 -1
  91. data/lib/oxidized/output/gitcrypt.rb +1 -1
  92. data/lib/oxidized/output/http.rb +12 -3
  93. data/lib/oxidized/source/csv.rb +5 -0
  94. data/lib/oxidized/source/jsonfile.rb +5 -0
  95. data/lib/oxidized/source/sql.rb +5 -0
  96. data/lib/oxidized/version.rb +2 -2
  97. data/lib/oxidized/worker.rb +36 -15
  98. data/lib/refinements.rb +18 -0
  99. data/oxidized.gemspec +28 -24
  100. metadata +103 -55
  101. data/docs/Model-Notes/APC_AOS.md +0 -65
  102. data/docs/Model-Notes/FortiOS.md +0 -44
@@ -1,54 +1,20 @@
1
1
  module Oxidized
2
2
  require 'net/ssh'
3
- require 'net/scp'
3
+ begin
4
+ require 'net/scp'
5
+ rescue LoadError
6
+ raise OxidizedError, 'net/scp not found: sudo gem install net-scp'
7
+ end
4
8
  require 'timeout'
5
- require_relative 'cli'
9
+ require_relative 'sshbase'
6
10
 
7
- class SCP < Input
11
+ class SCP < SSHBase
8
12
  RESCUE_FAIL = {
9
- debug: [
10
- Net::SSH::Disconnect,
11
- Net::SSH::ConnectionTimeout
12
- ],
13
- warn: [
14
- Net::SCP::Error,
15
- Net::SSH::HostKeyUnknown,
16
- Net::SSH::AuthenticationFailed,
17
- Timeout::Error
18
- ]
13
+ Net::SCP::Error => :warn
19
14
  }.freeze
20
- include Input::CLI
21
-
22
- def connect(node) # rubocop:disable Naming/PredicateMethod
23
- @node = node
24
- @node.model.cfg['scp'].each { |cb| instance_exec(&cb) }
25
- @log = File.open(Oxidized::Config::LOG + "/#{@node.ip}-scp", 'w') if Oxidized.config.input.debug?
26
- @ssh = Net::SSH.start(@node.ip, @node.auth[:username], make_ssh_opts)
27
- connected?
28
- end
29
-
30
- def make_ssh_opts
31
- secure = Oxidized.config.input.scp.secure?
32
- ssh_opts = {
33
- number_of_password_prompts: 0,
34
- verify_host_key: secure ? :always : :never,
35
- append_all_supported_algorithms: true,
36
- password: @node.auth[:password],
37
- timeout: @node.timeout,
38
- port: (vars(:ssh_port) || 22).to_i,
39
- forward_agent: false
40
- }
41
15
 
42
- # Use our logger for Net::SSH
43
- ssh_logger = SemanticLogger[Net::SSH]
44
- ssh_logger.level = Oxidized.config.input.debug? ? :debug : :fatal
45
- ssh_opts[:logger] = ssh_logger
46
-
47
- ssh_opts
48
- end
49
-
50
- def connected?
51
- @ssh && (not @ssh.closed?)
16
+ def self.rescue_fail
17
+ super.merge(RESCUE_FAIL)
52
18
  end
53
19
 
54
20
  def cmd(file)
@@ -57,25 +23,5 @@ module Oxidized
57
23
  @ssh.scp.download!(file)
58
24
  end
59
25
  end
60
-
61
- def send(my_proc)
62
- my_proc.call
63
- end
64
-
65
- def output
66
- ""
67
- end
68
-
69
- private
70
-
71
- def disconnect
72
- Timeout.timeout(@node.timeout) do
73
- @ssh.close
74
- end
75
- rescue Timeout::Error
76
- logger.debug "#{@node.name} timed out while disconnecting"
77
- ensure
78
- @log.close if Oxidized.config.input.debug?
79
- end
80
26
  end
81
27
  end
@@ -1,32 +1,28 @@
1
+ require 'timeout'
2
+ require_relative 'sshbase'
3
+ require_relative 'debugyaml'
4
+ require_relative 'debugtext'
5
+
1
6
  module Oxidized
2
- require 'net/ssh'
3
- require 'net/ssh/proxy/command'
4
- require 'timeout'
5
- require 'oxidized/input/cli'
6
- class SSH < Input
7
+ class SSH < SSHBase
8
+ class NoShell < OxidizedError; end
9
+
7
10
  RESCUE_FAIL = {
8
- debug: [
9
- Net::SSH::Disconnect
10
- ],
11
- warn: [
12
- RuntimeError,
13
- Net::SSH::AuthenticationFailed
14
- ]
11
+ RuntimeError => :warn
15
12
  }.freeze
16
- include Input::CLI
17
13
 
18
- class NoShell < OxidizedError; end
14
+ def self.rescue_fail
15
+ super.merge(RESCUE_FAIL)
16
+ end
19
17
 
20
18
  def connect(node) # rubocop:disable Naming/PredicateMethod
21
19
  @node = node
22
20
  @output = String.new('')
23
21
  @pty_options = { term: "vt100" }
24
22
  @node.model.cfg['ssh'].each { |cb| instance_exec(&cb) }
25
- if Oxidized.config.input.debug?
26
- logfile = Oxidized::Config::LOG + "/#{@node.ip}-ssh"
27
- @log = File.open(logfile, 'w')
28
- logger.debug "I/O Debuging to #{logfile}"
29
- end
23
+
24
+ @yaml_debug = DebugYAML.new(Oxidized.config.input.debug, @node, config_name)
25
+ @text_debug = DebugText.new(Oxidized.config.input.debug, @node, config_name)
30
26
 
31
27
  logger.debug "Connecting to #{@node.name}"
32
28
  @ssh = Net::SSH.start(@node.ip, @node.auth[:username], make_ssh_opts)
@@ -41,30 +37,31 @@ module Oxidized
41
37
  connected?
42
38
  end
43
39
 
44
- def connected?
45
- @ssh && (not @ssh.closed?)
46
- end
47
-
48
40
  def cmd(cmd, expect = node.prompt)
49
- logger.debug "Sending '#{cmd.dump}' @ #{node.name} with expect: #{expect.inspect}"
50
- if Oxidized.config.input.debug?
51
- @log.puts "sent cmd #{@exec ? cmd.dump : (cmd + newline).dump}"
52
- @log.flush
41
+ unless cmd.is_a?(String)
42
+ logger.error "cmd must be a String (#{cmd.class}): #{cmd.inspect} @ #{node.name}"
43
+ raise ArgumentError, "cmd must be a String"
53
44
  end
45
+ logger.debug "Sending '#{cmd.dump}' @ #{node.name} with expect: #{expect.inspect}"
54
46
  cmd_output = if @exec
47
+ @yaml_debug&.send_data(cmd)
48
+ @text_debug&.send_data(cmd)
55
49
  @ssh.exec! cmd
56
50
  else
57
51
  cmd_shell(cmd, expect).gsub("\r\n", "\n")
58
52
  end
53
+
54
+ # only logging @exec as cmd_shell is handled in the ssh loop
55
+ @yaml_debug&.receive_data(cmd_output) if @exec
56
+ @text_debug&.receive_data(cmd_output) if @exec
57
+
59
58
  # Make sure we return a String
60
59
  cmd_output.to_s
61
60
  end
62
61
 
63
62
  def send(data)
64
- if Oxidized.config.input.debug?
65
- @log.puts "sent data #{data.dump}"
66
- @log.flush
67
- end
63
+ @yaml_debug&.send_data(data)
64
+ @text_debug&.send_data(data)
68
65
  @ses.send_data data
69
66
  end
70
67
 
@@ -76,6 +73,7 @@ module Oxidized
76
73
 
77
74
  private
78
75
 
76
+ # We need a specific disconnect for SSH in shell mode, see issue #3725
79
77
  def disconnect
80
78
  disconnect_cli
81
79
  # if disconnect does not disconnect us, give up after timeout
@@ -86,17 +84,16 @@ module Oxidized
86
84
  rescue Timeout::Error
87
85
  logger.debug "#{@node.name} timed out while disconnecting"
88
86
  ensure
89
- @log.close if Oxidized.config.input.debug?
87
+ @yaml_debug&.close
88
+ @text_debug&.close
90
89
  (@ssh.close rescue true) unless @ssh.closed? # rubocop:disable Style/RedundantParentheses
91
90
  end
92
91
 
93
92
  def shell_open(ssh)
94
93
  @ses = ssh.open_channel do |ch|
95
94
  ch.on_data do |_ch, data|
96
- if Oxidized.config.input.debug?
97
- @log.puts "received #{data.dump}"
98
- @log.flush
99
- end
95
+ @yaml_debug&.receive_data(data)
96
+ @text_debug&.receive_data(data)
100
97
  @output << data
101
98
  @output = @node.model.expects @output
102
99
  end
@@ -118,6 +115,9 @@ module Oxidized
118
115
 
119
116
  def cmd_shell(cmd, expect_re)
120
117
  @output = String.new('')
118
+
119
+ @yaml_debug&.send_data(cmd + newline)
120
+ @text_debug&.send_data(cmd + newline)
121
121
  @ses.send_data cmd + newline
122
122
  @ses.process
123
123
  expect expect_re if expect_re
@@ -137,48 +137,5 @@ module Oxidized
137
137
  end
138
138
  end
139
139
  end
140
-
141
- def make_ssh_opts
142
- secure = Oxidized.config.input.ssh.secure?
143
- ssh_opts = {
144
- number_of_password_prompts: 0,
145
- keepalive: vars(:ssh_no_keepalive) ? false : true,
146
- verify_host_key: secure ? :always : :never,
147
- append_all_supported_algorithms: true,
148
- password: @node.auth[:password],
149
- timeout: @node.timeout,
150
- port: (vars(:ssh_port) || 22).to_i,
151
- forward_agent: false
152
- }
153
-
154
- auth_methods = vars(:auth_methods) || %w[none publickey password]
155
- ssh_opts[:auth_methods] = auth_methods
156
- logger.debug "AUTH METHODS::#{auth_methods}"
157
-
158
- ssh_opts[:proxy] = make_ssh_proxy_command(vars(:ssh_proxy), vars(:ssh_proxy_port), secure) if vars(:ssh_proxy)
159
-
160
- ssh_opts[:keys] = [vars(:ssh_keys)].flatten if vars(:ssh_keys)
161
- ssh_opts[:kex] = vars(:ssh_kex).split(/,\s*/) if vars(:ssh_kex)
162
- ssh_opts[:encryption] = vars(:ssh_encryption).split(/,\s*/) if vars(:ssh_encryption)
163
- ssh_opts[:host_key] = vars(:ssh_host_key).split(/,\s*/) if vars(:ssh_host_key)
164
- ssh_opts[:hmac] = vars(:ssh_hmac).split(/,\s*/) if vars(:ssh_hmac)
165
-
166
- # Use our logger for Net::SSH
167
- ssh_logger = SemanticLogger[Net::SSH]
168
- ssh_logger.level = Oxidized.config.input.debug? ? :debug : :fatal
169
- ssh_opts[:logger] = ssh_logger
170
-
171
- ssh_opts
172
- end
173
-
174
- def make_ssh_proxy_command(proxy_host, proxy_port, secure)
175
- return nil unless !proxy_host.nil? && !proxy_host.empty?
176
-
177
- proxy_command = "ssh "
178
- proxy_command += "-o StrictHostKeyChecking=no " unless secure
179
- proxy_command += "-p #{proxy_port} " if proxy_port
180
- proxy_command += "#{proxy_host} -W [%h]:%p"
181
- Net::SSH::Proxy::Command.new(proxy_command)
182
- end
183
140
  end
184
141
  end
@@ -0,0 +1,102 @@
1
+ module Oxidized
2
+ require 'net/ssh'
3
+ require 'net/ssh/proxy/command'
4
+ require 'timeout'
5
+
6
+ class SSHBase < Input
7
+ RESCUE_FAIL = {
8
+ Net::SSH::Disconnect => :debug,
9
+ Net::SSH::ConnectionTimeout => :debug,
10
+ Net::SSH::AuthenticationFailed => :warn,
11
+ Net::SSH::HostKeyUnknown => :warn
12
+ }.freeze
13
+
14
+ def self.rescue_fail
15
+ super.merge(RESCUE_FAIL)
16
+ end
17
+
18
+ def connect(node) # rubocop:disable Naming/PredicateMethod
19
+ @node = node
20
+ @node.model.cfg[config_name].each { |cb| instance_exec(&cb) }
21
+ logger.debug "Connecting to #{@node.name}"
22
+ @ssh = Net::SSH.start(@node.ip, @node.auth[:username], make_ssh_opts)
23
+ connected?
24
+ end
25
+
26
+ def connected?
27
+ @ssh && (not @ssh.closed?)
28
+ end
29
+
30
+ def make_ssh_opts
31
+ ssh_opts = {
32
+ number_of_password_prompts: 0,
33
+ keepalive: vars(:ssh_no_keepalive) ? false : true,
34
+ verify_host_key: must_secure? ? :always : :never,
35
+ append_all_supported_algorithms: true,
36
+ password: @node.auth[:password],
37
+ timeout: @node.timeout,
38
+ port: (vars(:ssh_port) || 22).to_i,
39
+ forward_agent: false
40
+ }
41
+
42
+ auth_methods = vars(:auth_methods) || %w[none publickey password]
43
+ ssh_opts[:auth_methods] = auth_methods
44
+ logger.debug "AUTH METHODS::#{auth_methods}"
45
+
46
+ if vars(:ssh_proxy)
47
+ ssh_opts[:proxy] = make_ssh_proxy_command(
48
+ vars(:ssh_proxy), vars(:ssh_proxy_port), must_secure?
49
+ )
50
+ end
51
+ ssh_opts[:keys] = [vars(:ssh_keys)].flatten if vars(:ssh_keys)
52
+ ssh_opts[:kex] = vars(:ssh_kex).split(/,\s*/) if vars(:ssh_kex)
53
+ ssh_opts[:encryption] = vars(:ssh_encryption).split(/,\s*/) if vars(:ssh_encryption)
54
+ ssh_opts[:host_key] = vars(:ssh_host_key).split(/,\s*/) if vars(:ssh_host_key)
55
+ ssh_opts[:hmac] = vars(:ssh_hmac).split(/,\s*/) if vars(:ssh_hmac)
56
+
57
+ # Use our logger for Net::SSH
58
+ ssh_logger = SemanticLogger[Net::SSH]
59
+ config_debug = Oxidized.config.input.debug
60
+ if config_debug == true ||
61
+ (config_debug.is_a?(String) && config_debug.downcase.include?('library'))
62
+ ssh_logger.level = :debug
63
+ else
64
+ ssh_logger.level = :fatal
65
+ end
66
+ ssh_opts[:logger] = ssh_logger
67
+
68
+ ssh_opts
69
+ end
70
+
71
+ def must_secure?
72
+ Oxidized.config.input[config_name].secure? == true
73
+ end
74
+
75
+ def make_ssh_proxy_command(proxy_host, proxy_port, secure)
76
+ return nil unless !proxy_host.nil? && !proxy_host.empty?
77
+
78
+ proxy_command = "ssh "
79
+ proxy_command += "-o StrictHostKeyChecking=no " unless secure
80
+ proxy_command += "-p #{proxy_port} " if proxy_port
81
+ proxy_command += "#{proxy_host} -W [%h]:%p"
82
+ Net::SSH::Proxy::Command.new(proxy_command)
83
+ end
84
+
85
+ def disconnect
86
+ disconnect_cli
87
+ Timeout.timeout(@node.timeout) do
88
+ @ssh.close
89
+ end
90
+ rescue Errno::ECONNRESET, Net::SSH::Disconnect, IOError => e
91
+ logger.debug 'The other side closed the connection while ' \
92
+ "disconnecting, raising #{e.class} with #{e.message}"
93
+ rescue Timeout::Error
94
+ logger.debug "#{@node.name} timed out while disconnecting"
95
+ end
96
+
97
+ # Methods to implement in subclasses
98
+ def cmd(**_args)
99
+ raise NotImplementedError, "Subclasses must implement cmd"
100
+ end
101
+ end
102
+ end
@@ -1,17 +1,17 @@
1
+ require_relative 'debugtext'
2
+
1
3
  module Oxidized
2
4
  require 'net/telnet'
3
- require 'oxidized/input/cli'
4
5
  class Telnet < Input
5
- RESCUE_FAIL = {}.freeze
6
- include Input::CLI
7
-
8
6
  attr_reader :telnet
9
7
 
10
8
  def connect(node) # rubocop:disable Naming/PredicateMethod
11
9
  @node = node
12
10
  @timeout = @node.timeout
13
11
  @node.model.cfg['telnet'].each { |cb| instance_exec(&cb) }
14
- @log = File.open(Oxidized::Config::LOG + "/#{@node.ip}-telnet", 'w') if Oxidized.config.input.debug?
12
+
13
+ @text_debug = DebugText.new(Oxidized.config.input.debug, @node, config_name)
14
+
15
15
  port = vars(:telnet_port) || 23
16
16
 
17
17
  telnet_opts = {
@@ -19,7 +19,7 @@ module Oxidized
19
19
  'Port' => port.to_i,
20
20
  'Timeout' => @timeout,
21
21
  'Model' => @node.model,
22
- 'Log' => @log
22
+ 'Log' => @text_debug
23
23
  }
24
24
 
25
25
  @telnet = Net::Telnet.new telnet_opts
@@ -42,12 +42,14 @@ module Oxidized
42
42
  # create a string to be passed to oxidized_expect and modified _there_
43
43
  # default to a single space so it shouldn't be coerced to nil by any models.
44
44
  out = String(' ')
45
+ @text_debug&.send_data(cmd_str)
45
46
  @telnet.puts(cmd_str)
46
47
  @telnet.oxidized_expect(timeout: @timeout, expect: expect, out: out)
47
48
  out
48
49
  end
49
50
 
50
51
  def send(data)
52
+ @text_debug&.send_data(data)
51
53
  @telnet.write data
52
54
  end
53
55
 
@@ -67,7 +69,7 @@ module Oxidized
67
69
  rescue Errno::ECONNRESET, IOError
68
70
  # This exception is intented and therefore not handled here
69
71
  ensure
70
- @log.close if Oxidized.config.input.debug?
72
+ @text_debug&.close
71
73
  (@telnet.close rescue true) unless @telnet.sock.closed? # rubocop:disable Style/RedundantParentheses
72
74
  end
73
75
  end
@@ -80,8 +82,8 @@ module Net
80
82
  attr_reader :output
81
83
 
82
84
  def oxidized_expect(options)
83
- model = @options["Model"]
84
- @log = @options["Log"]
85
+ model = @options["Model"]
86
+ text_debug = @options["Log"]
85
87
 
86
88
  expects = [options[:expect]].flatten
87
89
  time_out = options[:timeout] || @options["Timeout"]
@@ -107,10 +109,7 @@ module Net
107
109
  buf = preprocess(c)
108
110
  rest = ''
109
111
  end
110
- if Oxidized.config.input.debug?
111
- @log.print buf
112
- @log.flush
113
- end
112
+ text_debug&.receive_data buf
114
113
  line += buf
115
114
  line = model.expects line
116
115
  # match is a regexp object. we need to return that for logins to work.
@@ -1,7 +1,5 @@
1
1
  module Oxidized
2
2
  require 'stringio'
3
- require_relative 'cli'
4
-
5
3
  begin
6
4
  require 'net/tftp'
7
5
  rescue LoadError
@@ -9,14 +7,19 @@ module Oxidized
9
7
  end
10
8
 
11
9
  class TFTP < Input
12
- include Input::CLI
10
+ RESCUE_FAIL = {
11
+ Net::TFTPTimeout => :warn
12
+ }.freeze
13
+
14
+ def self.rescue_fail
15
+ super.merge(RESCUE_FAIL)
16
+ end
13
17
 
14
18
  # TFTP utilizes UDP, there is not a connection. We simply specify an IP and send/receive data.
15
19
  def connect(node)
16
20
  @node = node
17
21
 
18
22
  @node.model.cfg['tftp'].each { |cb| instance_exec(&cb) }
19
- @log = File.open(Oxidized::Config::LOG + "/#{@node.ip}-tftp", 'w') if Oxidized.config.input.debug?
20
23
  @tftp = Net::TFTP.new @node.ip
21
24
  end
22
25
 
@@ -32,9 +35,6 @@ module Oxidized
32
35
 
33
36
  def disconnect
34
37
  # TFTP uses UDP, there is no connection to close
35
- true
36
- ensure
37
- @log.close if Oxidized.config.input.debug?
38
38
  end
39
39
  end
40
40
  end
@@ -22,9 +22,11 @@ class Aoscx < Oxidized::Model
22
22
  end
23
23
 
24
24
  cmd :secret do |cfg|
25
+ cfg.gsub! /^(user .* group .*(?: ciphertext)?) \S+/, '\\1 <secret hidden>'
25
26
  cfg.gsub! /^(snmp-server community) \S+(.*)/, '\\1 <secret hidden> \\2'
26
27
  cfg.gsub! /^(snmp-server host \S+) \S+(.*)/, '\\1 <secret hidden> \\2'
27
- cfg.gsub! /^(radius-server host \S+ key) \S+(.*)/, '\\1 <secret hidden> \\2'
28
+ cfg.gsub! /^(snmpv3 user).*?(auth (?:md5|sha(?:\d{1,3})?) auth-pass ciphertext).*?(priv (?:des|aes(?:\d{1,3})?) priv-pass ciphertext).*/, '\\1 <user> \\2 <auth-pass> \\3 <priv-pass>'
29
+ cfg.gsub! /^(radius-server host \S+ key(?: ciphertext)?) \S+ (.*)/, '\\1 <secret hidden> \\2'
28
30
  cfg.gsub! /^(radius-server key).*/, '\\1 <configuration removed>'
29
31
  cfg.gsub! /^(tacacs-server host \S+ key) \S+(.*)/, '\\1 <secret hidden> \\2'
30
32
  cfg.gsub! /^(tacacs-server key).*/, '\\1 <secret hidden>'
@@ -35,22 +37,23 @@ class Aoscx < Oxidized::Model
35
37
  comment cfg
36
38
  end
37
39
 
38
- cmd 'show environment' do |cfg|
39
- def with_section(cfg, section, &block)
40
- cfg.sub!(/(show environment #{section}.*?-{10,}\n)(.*?)(?=\nshow environment|\z)/m) do
41
- header = ::Regexp.last_match(1)
42
- content = ::Regexp.last_match(2)
43
- block.call(content) if block_given?
44
- header + content
45
- end
40
+ def with_section(cfg, section, &block)
41
+ cfg.sub!(/(show environment #{section}.*?-{10,}\n)(.*?)(?=\nshow environment|\z)/m) do
42
+ header = ::Regexp.last_match(1)
43
+ content = ::Regexp.last_match(2)
44
+ block.call(content) if block_given?
45
+ header + content
46
46
  end
47
+ end
47
48
 
49
+ cmd 'show environment' do |cfg|
48
50
  with_section(cfg, 'fan') do |content|
49
- content.gsub!(/^(.*)(slow|normal|medium|fast|max) (.*?)\d+ +$/, '\\1<speed> \\3<rpm>')
51
+ content.gsub!(/^((?:\S+ +){3})(slow |normal|medium|fast |max |N\/A ) (.*?)\d+ +$/, '\\1<speed> \\3<rpm>')
50
52
  end
51
53
 
52
54
  with_section(cfg, 'power-consumption') do |content|
53
55
  content.gsub!(/^(.*?) (?:\d+\.\d+ +)+\d+\.\d+$/, '\\1 <power hidden>')
56
+ content.gsub!(/^(Total Power Consumption +)\d+\.\d+$/, '\\1<power hidden>')
54
57
  end
55
58
 
56
59
  with_section(cfg, 'power-allocation') do |content|
@@ -58,7 +61,7 @@ class Aoscx < Oxidized::Model
58
61
  end
59
62
 
60
63
  with_section(cfg, 'temperature') do |content|
61
- content.gsub!(/^(.*) \d+\.\d+ C (.*)$/, '\\1 <hidden>\\2')
64
+ content.gsub!(/^(.*) -?\d+\.\d+ C (.*)$/, '\\1 <hidden>\\2')
62
65
  end
63
66
  comment cfg
64
67
  end
@@ -71,7 +74,10 @@ class Aoscx < Oxidized::Model
71
74
  comment cfg
72
75
  end
73
76
 
74
- cmd 'show system | exclude "Up Time" | exclude "CPU" | exclude "Memory" | exclude "Pkts .x" | exclude "Lowest" | exclude "Missed"' do |cfg|
77
+ cmd 'show system' do |cfg|
78
+ cfg = cfg.reject_lines [
79
+ "Up Time", "CPU", "Memory", /Pkts .x/, "Lowest", "Missed"
80
+ ]
75
81
  comment cfg
76
82
  end
77
83
 
@@ -48,8 +48,11 @@ class AOSW < Oxidized::Model
48
48
  end
49
49
 
50
50
  cmd 'show version' do |cfg|
51
- cfg = cfg.each_line.reject { |line| line.match(/(Switch|AP) uptime/i) || line.match(/Reboot Time and Cause/i) }
52
- rstrip_cfg comment cfg.join
51
+ cfg = cfg.reject_lines [
52
+ /(Switch|AP) uptime/i,
53
+ /Reboot Time and Cause/i
54
+ ]
55
+ rstrip_cfg comment cfg
53
56
  end
54
57
 
55
58
  cmd 'show inventory' do |cfg|
@@ -77,15 +80,11 @@ class AOSW < Oxidized::Model
77
80
  end
78
81
 
79
82
  cmd 'show running-config' do |cfg|
80
- out = []
81
- cfg.each_line do |line|
82
- next if line =~ /^controller config \d+$/
83
- next if line =~ /^Building Configuration/
84
-
85
- out << line.strip
86
- end
87
- out = out.join "\n"
88
- out << "\n"
83
+ cfg = cfg.reject_lines [
84
+ /^controller config \d+$/,
85
+ /^Building Configuration/
86
+ ]
87
+ rstrip_cfg cfg
89
88
  end
90
89
 
91
90
  cfg :telnet do
@@ -1,7 +1,11 @@
1
1
  class Apc_aos < Oxidized::Model # rubocop:disable Naming/ClassAndModuleCamelCase
2
2
  using Refinements
3
3
 
4
+ comment '; '
5
+
4
6
  cmd 'config.ini' do |cfg|
7
+ logger.warn "Apc_aos is deprecated, use ApcAos instead."
8
+
5
9
  cfg.gsub!(/^; Configuration file, generated on.*\n/, '')
6
10
  cfg
7
11
  end
@@ -0,0 +1,39 @@
1
+ class ApcAos < Oxidized::Model
2
+ using Refinements
3
+
4
+ # Prompt can be short (apc>) or long (username@apc>)
5
+ prompt /^(:?\S+@)?apc>/
6
+ comment '; '
7
+
8
+ def clean(cfg)
9
+ cfg = cfg.cut_both(2, 1)
10
+ cfg.gsub("\r", "")
11
+ end
12
+
13
+ cmd 'about', input: :ssh do |cfg|
14
+ cfg = clean(cfg)
15
+ cfg = cfg.reject_lines [/^Management Uptime: /, /^Date: /, /^Time: /]
16
+ comment cfg
17
+ end
18
+
19
+ cmd 'upsabout', input: :ssh do |cfg|
20
+ cfg = clean(cfg)
21
+ comment cfg
22
+ end
23
+
24
+ cmd 'detstatus -ss', input: :ssh do |cfg|
25
+ cfg = clean(cfg)
26
+ comment cfg
27
+ end
28
+
29
+ cmd 'config.ini', input: %i[scp ftp] do |cfg|
30
+ cfg = cfg.reject_lines [/^; Configuration file, generated on /]
31
+ "; ========== config.ini ==========\n" + cfg
32
+ end
33
+
34
+ inputs [:ssh, %i[scp ftp]]
35
+
36
+ cfg :ssh do
37
+ pre_logout 'exit'
38
+ end
39
+ end
@@ -31,30 +31,21 @@ class ArubaInstant < Oxidized::Model
31
31
 
32
32
  # get software version
33
33
  cmd 'show version' do |cfg|
34
- out = ''
35
- cfg.each_line do |line|
36
- next if line =~ /^(Switch|AP) uptime is /
37
-
38
- next if line =~ /^Reboot Time and Cause/
39
-
40
- out += line
41
- end
42
- comment out
34
+ cfg = cfg.reject_lines [
35
+ /^(Switch|AP) uptime is /,
36
+ /^Reboot Time and Cause/
37
+ ]
38
+ comment cfg
43
39
  end
44
40
 
45
41
  # Get serial number
46
42
  cmd 'show activate status' do |cfg|
47
- out = ''
48
- cfg.each_line do |line|
49
- next if line =~ /^Activate /
50
-
51
- next if line =~ /^Provision interval/
52
-
53
- next if line =~ /^Cloud Activation Key/
54
-
55
- out += line
56
- end
57
- comment out + "\n"
43
+ cfg = cfg.reject_lines [
44
+ /^Activate /,
45
+ /^Provision interval/,
46
+ /^Cloud Activation Key/
47
+ ]
48
+ comment cfg + "\n"
58
49
  end
59
50
 
60
51
  # Get controlled WLAN-AP