oxidized 0.34.3 → 0.36.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 (126) hide show
  1. checksums.yaml +4 -4
  2. data/.coderabbit.yaml +21 -0
  3. data/.github/workflows/codeql.yml +4 -4
  4. data/.github/workflows/publishdocker.yml +88 -15
  5. data/.github/workflows/ruby.yml +2 -4
  6. data/.github/workflows/stale.yml +1 -1
  7. data/.rubocop.yml +14 -2
  8. data/.rubocop_todo.yml +21 -2
  9. data/CHANGELOG.md +86 -1
  10. data/Dockerfile +74 -66
  11. data/README.md +4 -5
  12. data/Rakefile +21 -3
  13. data/docs/Configuration.md +122 -1
  14. data/docs/Creating-Models.md +209 -28
  15. data/docs/Docker.md +2 -1
  16. data/docs/Inputs.md +29 -0
  17. data/docs/Model-Notes/APC.md +72 -0
  18. data/docs/Model-Notes/ExaLink.md +43 -0
  19. data/docs/Model-Notes/Fortinet.md +75 -0
  20. data/docs/Model-Notes/IvantiConnectSecure.md +59 -0
  21. data/docs/Model-Notes/TrueNAS.md +19 -0
  22. data/docs/ModelUnitTests.md +23 -0
  23. data/docs/Outputs.md +18 -4
  24. data/docs/Release.md +1 -1
  25. data/docs/Ruby-API.md +134 -1
  26. data/docs/Supported-OS-Types.md +22 -8
  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 +168 -0
  31. data/extra/hooks/srcipmap.rb +54 -0
  32. data/extra/oxidized.runit +1 -1
  33. data/lib/oxidized/core.rb +2 -1
  34. data/lib/oxidized/hook/githubrepo.rb +2 -1
  35. data/lib/oxidized/hook.rb +56 -8
  36. data/lib/oxidized/input/exec.rb +0 -4
  37. data/lib/oxidized/input/ftp.rb +0 -13
  38. data/lib/oxidized/input/http.rb +47 -15
  39. data/lib/oxidized/input/input.rb +33 -13
  40. data/lib/oxidized/input/scp.rb +11 -65
  41. data/lib/oxidized/input/ssh.rb +12 -62
  42. data/lib/oxidized/input/sshbase.rb +107 -0
  43. data/lib/oxidized/input/telnet.rb +2 -6
  44. data/lib/oxidized/input/tftp.rb +7 -3
  45. data/lib/oxidized/model/adva.rb +1 -1
  46. data/lib/oxidized/model/airfiber.rb +1 -1
  47. data/lib/oxidized/model/aoscx.rb +32 -45
  48. data/lib/oxidized/model/aosw.rb +11 -12
  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 +29 -26
  52. data/lib/oxidized/model/asa.rb +7 -7
  53. data/lib/oxidized/model/asternos.rb +1 -1
  54. data/lib/oxidized/model/axos.rb +1 -1
  55. data/lib/oxidized/model/comtrol.rb +1 -1
  56. data/lib/oxidized/model/comware.rb +3 -1
  57. data/lib/oxidized/model/cumulus.rb +2 -7
  58. data/lib/oxidized/model/defacto.rb +26 -0
  59. data/lib/oxidized/model/dslcommands.rb +93 -0
  60. data/lib/oxidized/model/dslsetup.rb +102 -0
  61. data/lib/oxidized/model/eatonnetwork.rb +5 -0
  62. data/lib/oxidized/model/efos.rb +5 -5
  63. data/lib/oxidized/model/eltex.rb +1 -1
  64. data/lib/oxidized/model/enterasys800.rb +1 -1
  65. data/lib/oxidized/model/exalink.rb +36 -0
  66. data/lib/oxidized/model/fabricos.rb +1 -1
  67. data/lib/oxidized/model/fastiron.rb +2 -2
  68. data/lib/oxidized/model/firelinuxos.rb +1 -3
  69. data/lib/oxidized/model/fortigate.rb +160 -0
  70. data/lib/oxidized/model/fortios.rb +28 -69
  71. data/lib/oxidized/model/fsos.rb +2 -4
  72. data/lib/oxidized/model/garderos.rb +1 -6
  73. data/lib/oxidized/model/h3c.rb +1 -1
  74. data/lib/oxidized/model/ios.rb +21 -15
  75. data/lib/oxidized/model/ironware.rb +6 -4
  76. data/lib/oxidized/model/ivanti.rb +54 -0
  77. data/lib/oxidized/model/macros.rb +60 -0
  78. data/lib/oxidized/model/mlnxos.rb +14 -17
  79. data/lib/oxidized/model/model.rb +89 -98
  80. data/lib/oxidized/model/ndms.rb +6 -0
  81. data/lib/oxidized/model/netgear.rb +6 -3
  82. data/lib/oxidized/model/netonix.rb +1 -1
  83. data/lib/oxidized/model/netscaler.rb +1 -1
  84. data/lib/oxidized/model/nxos.rb +9 -9
  85. data/lib/oxidized/model/onefinity.rb +1 -1
  86. data/lib/oxidized/model/opnsense.rb +8 -0
  87. data/lib/oxidized/model/outputs.rb +5 -0
  88. data/lib/oxidized/model/perle.rb +41 -0
  89. data/lib/oxidized/model/pfsense.rb +8 -0
  90. data/lib/oxidized/model/saos10.rb +1 -1
  91. data/lib/oxidized/model/siklu.rb +1 -1
  92. data/lib/oxidized/model/siklumhtg.rb +1 -1
  93. data/lib/oxidized/model/sixwind.rb +1 -1
  94. data/lib/oxidized/model/slxos.rb +1 -1
  95. data/lib/oxidized/model/smartbyte.rb +48 -0
  96. data/lib/oxidized/model/sonicos.rb +1 -1
  97. data/lib/oxidized/model/speedtouch.rb +1 -1
  98. data/lib/oxidized/model/telco.rb +1 -1
  99. data/lib/oxidized/model/tnsr.rb +7 -3
  100. data/lib/oxidized/model/trango.rb +1 -1
  101. data/lib/oxidized/model/truenas.rb +10 -1
  102. data/lib/oxidized/model/ucs.rb +1 -1
  103. data/lib/oxidized/model/voltaire.rb +1 -1
  104. data/lib/oxidized/model/voss.rb +3 -0
  105. data/lib/oxidized/model/vyatta.rb +1 -1
  106. data/lib/oxidized/model/vyos.rb +36 -0
  107. data/lib/oxidized/model/zhoneolt.rb +1 -1
  108. data/lib/oxidized/model/zynoscli.rb +1 -1
  109. data/lib/oxidized/node.rb +31 -24
  110. data/lib/oxidized/nodes.rb +2 -0
  111. data/lib/oxidized/output/file.rb +7 -1
  112. data/lib/oxidized/output/git.rb +11 -1
  113. data/lib/oxidized/output/gitcrypt.rb +1 -1
  114. data/lib/oxidized/output/http.rb +12 -3
  115. data/lib/oxidized/source/csv.rb +5 -0
  116. data/lib/oxidized/source/http.rb +10 -2
  117. data/lib/oxidized/source/jsonfile.rb +5 -0
  118. data/lib/oxidized/source/sql.rb +5 -0
  119. data/lib/oxidized/version.rb +2 -2
  120. data/lib/oxidized/worker.rb +36 -15
  121. data/lib/refinements.rb +18 -0
  122. data/oxidized.gemspec +28 -25
  123. metadata +98 -71
  124. data/docs/Model-Notes/APC_AOS.md +0 -65
  125. data/docs/Model-Notes/FortiOS.md +0 -44
  126. data/lib/oxidized/model/timos.rb +0 -10
@@ -1,21 +1,17 @@
1
1
  module Oxidized
2
- require 'net/ssh'
3
- require 'net/ssh/proxy/command'
4
2
  require 'timeout'
5
- require 'oxidized/input/cli'
6
- class SSH < Input
3
+ require_relative 'sshbase'
4
+
5
+ class SSH < SSHBase
6
+ class NoShell < OxidizedError; end
7
+
7
8
  RESCUE_FAIL = {
8
- debug: [
9
- Net::SSH::Disconnect
10
- ],
11
- warn: [
12
- RuntimeError,
13
- Net::SSH::AuthenticationFailed
14
- ]
9
+ RuntimeError => :warn
15
10
  }.freeze
16
- include Input::CLI
17
11
 
18
- class NoShell < OxidizedError; end
12
+ def self.rescue_fail
13
+ super.merge(RESCUE_FAIL)
14
+ end
19
15
 
20
16
  def connect(node) # rubocop:disable Naming/PredicateMethod
21
17
  @node = node
@@ -41,10 +37,6 @@ 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
41
  logger.debug "Sending '#{cmd.dump}' @ #{node.name} with expect: #{expect.inspect}"
50
42
  if Oxidized.config.input.debug?
@@ -76,10 +68,11 @@ module Oxidized
76
68
 
77
69
  private
78
70
 
71
+ # We need a specific disconnect for SSH in shell mode, see issue #3725
79
72
  def disconnect
80
73
  disconnect_cli
81
74
  # if disconnect does not disconnect us, give up after timeout
82
- Timeout.timeout(Oxidized.config.timeout) { @ssh.loop }
75
+ Timeout.timeout(@node.timeout) { @ssh.loop }
83
76
  rescue Errno::ECONNRESET, Net::SSH::Disconnect, IOError => e
84
77
  logger.debug 'The other side closed the connection while ' \
85
78
  "disconnecting, raising #{e.class} with #{e.message}"
@@ -127,7 +120,7 @@ module Oxidized
127
120
  def expect(*regexps)
128
121
  regexps = [regexps].flatten
129
122
  logger.debug "Expecting #{regexps.inspect} at #{node.name}"
130
- Timeout.timeout(Oxidized.config.timeout) do
123
+ Timeout.timeout(@node.timeout) do
131
124
  @ssh.loop(0.1) do
132
125
  sleep 0.1
133
126
  match = regexps.find { |regexp| @output.match regexp }
@@ -137,48 +130,5 @@ module Oxidized
137
130
  end
138
131
  end
139
132
  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: Oxidized.config.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
133
  end
184
134
  end
@@ -0,0 +1,107 @@
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
+ setup_debug_logging
22
+ logger.debug "Connecting to #{@node.name}"
23
+ @ssh = Net::SSH.start(@node.ip, @node.auth[:username], make_ssh_opts)
24
+ connected?
25
+ end
26
+
27
+ def connected?
28
+ @ssh && (not @ssh.closed?)
29
+ end
30
+
31
+ def setup_debug_logging
32
+ return unless Oxidized.config.input.debug?
33
+
34
+ logfile = Oxidized::Config::LOG + "/#{@node.ip}-#{config_name}"
35
+ @log = File.open(logfile, 'w')
36
+ logger.debug "I/O Debugging to #{logfile}"
37
+ end
38
+
39
+ def make_ssh_opts
40
+ ssh_opts = {
41
+ number_of_password_prompts: 0,
42
+ keepalive: vars(:ssh_no_keepalive) ? false : true,
43
+ verify_host_key: must_secure? ? :always : :never,
44
+ append_all_supported_algorithms: true,
45
+ password: @node.auth[:password],
46
+ timeout: @node.timeout,
47
+ port: (vars(:ssh_port) || 22).to_i,
48
+ forward_agent: false
49
+ }
50
+
51
+ auth_methods = vars(:auth_methods) || %w[none publickey password]
52
+ ssh_opts[:auth_methods] = auth_methods
53
+ logger.debug "AUTH METHODS::#{auth_methods}"
54
+
55
+ if vars(:ssh_proxy)
56
+ ssh_opts[:proxy] = make_ssh_proxy_command(
57
+ vars(:ssh_proxy), vars(:ssh_proxy_port), must_secure?
58
+ )
59
+ end
60
+ ssh_opts[:keys] = [vars(:ssh_keys)].flatten if vars(:ssh_keys)
61
+ ssh_opts[:kex] = vars(:ssh_kex).split(/,\s*/) if vars(:ssh_kex)
62
+ ssh_opts[:encryption] = vars(:ssh_encryption).split(/,\s*/) if vars(:ssh_encryption)
63
+ ssh_opts[:host_key] = vars(:ssh_host_key).split(/,\s*/) if vars(:ssh_host_key)
64
+ ssh_opts[:hmac] = vars(:ssh_hmac).split(/,\s*/) if vars(:ssh_hmac)
65
+
66
+ # Use our logger for Net::SSH
67
+ ssh_logger = SemanticLogger[Net::SSH]
68
+ ssh_logger.level = Oxidized.config.input.debug? ? :debug : :fatal
69
+ ssh_opts[:logger] = ssh_logger
70
+
71
+ ssh_opts
72
+ end
73
+
74
+ def must_secure?
75
+ Oxidized.config.input[config_name].secure? == true
76
+ end
77
+
78
+ def make_ssh_proxy_command(proxy_host, proxy_port, secure)
79
+ return nil unless !proxy_host.nil? && !proxy_host.empty?
80
+
81
+ proxy_command = "ssh "
82
+ proxy_command += "-o StrictHostKeyChecking=no " unless secure
83
+ proxy_command += "-p #{proxy_port} " if proxy_port
84
+ proxy_command += "#{proxy_host} -W [%h]:%p"
85
+ Net::SSH::Proxy::Command.new(proxy_command)
86
+ end
87
+
88
+ def disconnect
89
+ disconnect_cli
90
+ Timeout.timeout(@node.timeout) do
91
+ @ssh.close
92
+ end
93
+ rescue Errno::ECONNRESET, Net::SSH::Disconnect, IOError => e
94
+ logger.debug 'The other side closed the connection while ' \
95
+ "disconnecting, raising #{e.class} with #{e.message}"
96
+ rescue Timeout::Error
97
+ logger.debug "#{@node.name} timed out while disconnecting"
98
+ ensure
99
+ @log.close if Oxidized.config.input.debug?
100
+ end
101
+
102
+ # Methods to implement in subclasses
103
+ def cmd(**_args)
104
+ raise NotImplementedError, "Subclasses must implement cmd"
105
+ end
106
+ end
107
+ end
@@ -1,15 +1,11 @@
1
1
  module Oxidized
2
2
  require 'net/telnet'
3
- require 'oxidized/input/cli'
4
3
  class Telnet < Input
5
- RESCUE_FAIL = {}.freeze
6
- include Input::CLI
7
-
8
4
  attr_reader :telnet
9
5
 
10
6
  def connect(node) # rubocop:disable Naming/PredicateMethod
11
7
  @node = node
12
- @timeout = Oxidized.config.timeout
8
+ @timeout = @node.timeout
13
9
  @node.model.cfg['telnet'].each { |cb| instance_exec(&cb) }
14
10
  @log = File.open(Oxidized::Config::LOG + "/#{@node.ip}-telnet", 'w') if Oxidized.config.input.debug?
15
11
  port = vars(:telnet_port) || 23
@@ -84,7 +80,7 @@ module Net
84
80
  @log = @options["Log"]
85
81
 
86
82
  expects = [options[:expect]].flatten
87
- time_out = options[:timeout] || @options["Timeout"] || Oxidized.config.timeout?
83
+ time_out = options[:timeout] || @options["Timeout"]
88
84
 
89
85
  Timeout.timeout(time_out) do
90
86
  line = ""
@@ -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,7 +7,13 @@ 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)
@@ -44,7 +44,7 @@ class ADVA < Oxidized::Model
44
44
 
45
45
  cmd 'show ports' do |cfg|
46
46
  cfg.each_line do |line|
47
- port = line.match(/\|((access|network)[^\|]+)\|/)
47
+ port = line.match(/\|((access|network)[^|]+)\|/)
48
48
  ports << port if port
49
49
  end
50
50
  end
@@ -3,7 +3,7 @@ class Airfiber < Oxidized::Model
3
3
 
4
4
  # Ubiquiti Airfiber (tested with Airfiber 11FX)
5
5
 
6
- prompt /^AF[\w\.-]+#/i
6
+ prompt /^AF[\w.-]+#/i
7
7
 
8
8
  cmd :all do |cfg|
9
9
  cfg.cut_both
@@ -1,25 +1,13 @@
1
1
  class Aoscx < Oxidized::Model
2
2
  using Refinements
3
+ # HPE Aruba Networking - ArubaOS-CX models
3
4
 
4
- # previous command is repeated followed by "\eE", which sometimes ends up on last line
5
- # ssh switches prompt may start with \r, followed by the prompt itself, regex ([\w\s.-]+[#>] ), which ends the line
6
- # telnet switchs may start with various vt100 control characters, regex (\e\[24;[0-9][hH]), follwed by the prompt, followed
7
- # by at least 3 other vt100 characters
8
- prompt /(^\r|\e\[24;[0-9][hH])?([\w\s.-]+[#>] )($|(\e\[24;[0-9][0-9]?[hH]){3})/
5
+ prompt /^[\w\s.-]+[#>] $/
6
+ clean :escape_codes
9
7
 
10
8
  comment '! '
11
9
 
12
- # replace next line control sequence with a new line
13
- expect /(\e\[1M\e\[\??\d+(;\d+)*[A-Za-z]\e\[1L)|(\eE)/ do |data, re|
14
- data.gsub re, "\n"
15
- end
16
-
17
- # replace all used vt100 control sequences
18
- expect /\e\[\??\d+(;\d+)*[A-Za-z]/ do |data, re|
19
- data.gsub re, ''
20
- end
21
-
22
- expect /Press any key to continue(\e\[\??\d+(;\d+)*[A-Za-z])*$/ do
10
+ expect /Press any key to continue$/ do
23
11
  send ' '
24
12
  ""
25
13
  end
@@ -30,19 +18,15 @@ class Aoscx < Oxidized::Model
30
18
  end
31
19
 
32
20
  cmd :all do |cfg|
33
- cfg = cfg.cut_both
34
- cfg = cfg.gsub /^\r/, ''
35
- # Additional filtering for elder switches sending vt100 control chars via telnet
36
- cfg.gsub! /\e\[\??\d+(;\d+)*[A-Za-z]/, ''
37
- # Additional filtering for power usage reporting which obviously changes over time
38
- cfg.gsub! /^(.*AC [0-9]{3}V\/?([0-9]{3}V)?) *([0-9]{1,3}) (.*)/, '\\1 <removed> \\4'
39
- cfg
21
+ cfg.cut_both
40
22
  end
41
23
 
42
24
  cmd :secret do |cfg|
25
+ cfg.gsub! /^(user .* group .*(?: ciphertext)?) \S+/, '\\1 <secret hidden>'
43
26
  cfg.gsub! /^(snmp-server community) \S+(.*)/, '\\1 <secret hidden> \\2'
44
27
  cfg.gsub! /^(snmp-server host \S+) \S+(.*)/, '\\1 <secret hidden> \\2'
45
- 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'
46
30
  cfg.gsub! /^(radius-server key).*/, '\\1 <configuration removed>'
47
31
  cfg.gsub! /^(tacacs-server host \S+ key) \S+(.*)/, '\\1 <secret hidden> \\2'
48
32
  cfg.gsub! /^(tacacs-server key).*/, '\\1 <secret hidden>'
@@ -54,27 +38,30 @@ class Aoscx < Oxidized::Model
54
38
  end
55
39
 
56
40
  cmd 'show environment' do |cfg|
57
- cfg.gsub! /^(LC.*\s+)\d+\s+$/, '\\1<hidden>'
58
- cfg.gsub! /^(\d\/\d\/\d.*\s+)\d+\s+$/, '\\1<hidden>'
59
- cfg.gsub! /^(\d+\/?\S+\s+\S+\s+)\d+\.\d+\s+C\s+(.*)/, '\\1<hidden> \\2'
60
- cfg.gsub! /^(LC.*\s+)\d+\.\d+\s+(C.*)$/, '\\1 <hidden> \\2'
61
- # match show environment No speed shown for switches CX83xx, e.g. "PSU-1/1/1 N/A N/A N/A front-to-back ok 7360"
62
- cfg.gsub! /^PSU(\S+\s+\S+\s+\s+\S+\s+)(slow|normal|medium|fast|max|N\/A)\s+(\S+\s+\S+\s+)\d+[[:blank:]]+/, '\\1<speed> \\3<rpm>'
63
- cfg.gsub! /^(\S+\s+\S+\s+\s+\S+\s+)(slow|normal|medium|fast|max)\s+(\S+\s+\S+\s+)\d+[[:blank:]]+/, '\\1<speed> \\3<rpm>'
64
- # match show environment power-consumption on VSF or standadlone, non-chassis and non-6400 switch, e.g. "2 6300M 48G 4SFP56 Swch 156.00 155.94"
65
- cfg.gsub! /^(\d+\s+.+\s+)(\s{2}\d{2}\.\d{2}|\s{1}\d{3}\.\d{2}|\d{4}\.\d{2})(\s+)(\s{2}\d{2}\.\d{2}|\s{1}\d{3}\.\d{2}|\d{4}\.\d{2})$/, '\\1<power>\\3<power>'
66
- # match show environment power-consumption on 6400 or chassis switches, e.g. "1/4 line-card-module R0X39A 6400 48p 1GbE CL4 PoE 4SFP56 Mod 54 W"
67
- cfg.gsub! /^(\d+\/?\d*\s+.+\s+)(\s{1,4}\d{1,3})\sW\s*$/, '\\1<power>'
68
- # match show environment power-consumption on 6400 or chassis switches, e.g. "Module Total Power Usage 13000 W", match up to a 5-digit number and keep table formatting.
69
- cfg.gsub! /^(Module|Chassis)\s(Total\sPower\sUsage)(\s+)\s(\s{4}\d{1}|\s{3}\d{2}|\s{2}\d{3}|\s{1}\d{4}|\d{5})\sW\s*$/, '\\1 <power>'
70
- # match show environment power-consumption on 6400 or chassis switches, e.g. "Chassis Total Power Usage 13000 W", match up to a 5-digit number and keep table formatting.
71
- cfg.gsub! /^(Chassis\sTotal\sPower\sUsage)(\s+)(\s{4}\d{1}|\s{3}\d{2}|\s{2}\d{3}|\s{1}\d{4}|\d{5})\sW\s*$/, '\\1\\2<power>'
72
- # match show environment power-consumption on 8400 or chassis switches, up to a 5-digit number, example matches:
73
- # e.g. "Chassis Total Power Allocated (total of all max wattages) 4130 W"
74
- # e.g. "Chassis Total Power Unallocated 15860 W"
75
- cfg.gsub! /^(Chassis\sTotal\sPower\s)(Allocated|Unallocated)(\s|\s\(total of all max wattages\))(\s+)(\s{4}\d{1}|\s{3}\d{2}|\s{2}\d{3}|\s{1}\d{4}|\d{5})\sW\s*$/, '\\1\\2\\3\\4<power>'
76
- # match Total Power Consumption:
77
- cfg.gsub! /^([t|T]otal\s[p|P]ower\s[c|C]onsumption\s+)(\d+\.\d\d)$/, '\\1<power>'
41
+ def with_section(cfg, section, &block)
42
+ cfg.sub!(/(show environment #{section}.*?-{10,}\n)(.*?)(?=\nshow environment|\z)/m) do
43
+ header = ::Regexp.last_match(1)
44
+ content = ::Regexp.last_match(2)
45
+ block.call(content) if block_given?
46
+ header + content
47
+ end
48
+ end
49
+
50
+ with_section(cfg, 'fan') do |content|
51
+ content.gsub!(/^((?:\S+ +){3})(slow |normal|medium|fast |max |N\/A ) (.*?)\d+ +$/, '\\1<speed> \\3<rpm>')
52
+ end
53
+
54
+ with_section(cfg, 'power-consumption') do |content|
55
+ content.gsub!(/^(.*?) (?:\d+\.\d+ +)+\d+\.\d+$/, '\\1 <power hidden>')
56
+ end
57
+
58
+ with_section(cfg, 'power-allocation') do |content|
59
+ content.gsub!(/^(.*) \d+ W$/, '\\1 <power>')
60
+ end
61
+
62
+ with_section(cfg, 'temperature') do |content|
63
+ content.gsub!(/^(.*) -?\d+\.\d+ C (.*)$/, '\\1 <hidden>\\2')
64
+ end
78
65
  comment cfg
79
66
  end
80
67
 
@@ -13,7 +13,7 @@ class AOSW < Oxidized::Model
13
13
 
14
14
  comment '# '
15
15
  # see /spec/model/aosw_spec.rb for prompt examples
16
- prompt /^\(?[\w\:.@-]+\)? ?[*^]?(\[[\w\/]+\] ?)?[#>] ?$/
16
+ prompt /^\(?[\w:.@-]+\)? ?[*^]?(\[[\w\/]+\] ?)?[#>] ?$/
17
17
 
18
18
  # Ignore cariage returns - also for the prompt
19
19
  expect "\r" do |data, re|
@@ -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
@@ -4,7 +4,7 @@ class ArubaInstant < Oxidized::Model
4
4
  # Aruba IAP, Instant Controller
5
5
 
6
6
  comment '# '
7
- prompt(/^ ?[\w\:.@-]+[#>] $/)
7
+ prompt(/^ ?[\w:.@-]+[#>] $/)
8
8
 
9
9
  cmd :all do |cfg|
10
10
  # Remove command echo and prompt
@@ -31,41 +31,44 @@ 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
61
52
  cmd 'show aps' do |cfg|
62
53
  out = ''
63
54
  cfg.each_line do |line|
64
- out += if line.match?(/^Name/)
65
- line.sub(/^(Name +IP Address +).*(Type +IPv6 Address +).*(Serial #).*$/, '\1\2\3')
66
- else
67
- line.sub(/^(\S+ +\S+ +)(?:\S+ +){3}(\S+ +\S+ +)(?:\S+ +){2}(\S+) +.*$/, '\1\2\3')
68
- end
55
+ out += line.sub(
56
+ /^(?'Name'(?:.+?|-{2,})\s{2,}) # \s{2,} = separator between columns
57
+ (?'IPv4'(?:
58
+ IP\ Address|-{2,}| # Header
59
+ (?:\d+\.){3}\S+ # Match an IPv4 to catch AP-Names with two spaces
60
+ )\s{2,})
61
+ (?:(?:.+?|-{2,})\s{2,}){3} # Ignore Mode, Spectrum, Clients
62
+ (?'Type'(?:.+?|-{2,})\s{2,})
63
+ (?'IPv6'(?:.+?|-{2,})\s{2,})
64
+ (?:(?:.+?|-{2,})\s{2,}) # Ignore Mesh Role
65
+ (?'Zone'(?:.+?|-{2,})\s{2,})
66
+ (?'Serial'(?:.+?|-{2,}))
67
+ \s{2,} # Last separator
68
+ .*$ # Ignore the rest
69
+ /x,
70
+ '\k<Name>\k<IPv4>\k<Type>\k<IPv6>\k<Zone>\k<Serial>'
71
+ )
69
72
  end
70
73
  comment out + "\n"
71
74
  end
@@ -32,13 +32,13 @@ class ASA < Oxidized::Model
32
32
  end
33
33
 
34
34
  cmd 'show version' do |cfg|
35
- # avoid commits due to uptime / ixo-router01 up 2 mins 28 secs / ixo-router01 up 1 days 2 hours
36
- cfg = cfg.each_line.reject { |line| line.match /(\s+up\s+\d+\s+)|(.*days.*)/ }
37
- cfg = cfg.join
38
- cfg.gsub! /^Configuration has not been modified since last system restart.*\n/, ''
39
- cfg.gsub! /^Configuration last modified by.*\n/, ''
40
- cfg.gsub! /^Start-up time.*\n/, ''
41
- comment cfg
35
+ comment cfg.reject_lines [
36
+ # avoid commits due to uptime / ixo-router01 up 2 mins 28 secs / ixo-router01 up 1 days 2 hours
37
+ /(\s+up\s+\d+\s+)|(.*days.*)/,
38
+ /^Configuration has not been modified since last system restart.*/,
39
+ /^Configuration last modified by.*/,
40
+ /^Start-up time.*/
41
+ ]
42
42
  end
43
43
 
44
44
  cmd 'show inventory' do |cfg|
@@ -1,7 +1,7 @@
1
1
  class AsterNOS < Oxidized::Model
2
2
  using Refinements
3
3
 
4
- prompt /^[^\$]+\$/
4
+ prompt /^[^$]+\$/
5
5
  comment '# '
6
6
 
7
7
  cmd :all do |cfg|
@@ -1,7 +1,7 @@
1
1
  class AxOS < Oxidized::Model
2
2
  using Refinements
3
3
 
4
- prompt /(\x1b\[\?7h)?([\w.@()-]+[#]\s?)$/
4
+ prompt /(\x1b\[\?7h)?([\w.@()-]+\#\s?)$/
5
5
  comment '! '
6
6
 
7
7
  cmd 'show running-config | nomore' do |cfg|
@@ -35,7 +35,7 @@ class Comtrol < Oxidized::Model
35
35
  expect /[pP]assword:\s?$/
36
36
  send vars(:enable) + "\n"
37
37
  end
38
- expect /^.+[#]\s?$/
38
+ expect /^.+\#\s?$/
39
39
  end
40
40
  end
41
41
  pre_logout 'exit'
@@ -21,8 +21,10 @@ class Comware < Oxidized::Model
21
21
 
22
22
  cmd :secret do |cfg|
23
23
  cfg.gsub! /^( snmp-agent community).*/, '\\1 <configuration removed>'
24
- cfg.gsub! /^( password hash).*/, '\\1 <configuration removed>'
24
+ cfg.gsub! /(password hash).*/, '\\1 <configuration removed>'
25
25
  cfg.gsub! /^( password cipher).*/, '\\1 <configuration removed>'
26
+ cfg.gsub! /(key (authentication )?cipher) \S+/, '\\1 <configuration removed>'
27
+ cfg.gsub! /(cipher authentication-mode (md5|sha)).\S+ (privacy-mode (des56|3des|aes\d{0,3})) .\S+/, '\\1 <auth-pass> \\3 <priv-pass>'
26
28
  cfg
27
29
  end
28
30
 
@@ -1,19 +1,14 @@
1
1
  class Cumulus < Oxidized::Model
2
2
  using Refinements
3
3
 
4
- # Remove ANSI escape codes
5
- expect /\e\[[0-?]*[ -\/]*[@-~]\r?/ do |data, re|
6
- data.gsub re, ''
7
- end
8
-
9
- # The prompt contains ANSI escape codes, which have already been removed
10
- # from the expect call above
4
+ # Regular expression explanation:
11
5
  # ^ : match begin of line, to have the most specific prompt
12
6
  # [\w.-]+@[\w.-]+ : user@hostname
13
7
  # (:mgmt)? : optional when logged in out of band
14
8
  # :~[#$] $ : end of prompt, containing the linux path,
15
9
  # which is always "~" in our context
16
10
  prompt /^[\w.-]+@[\w.-]+(:mgmt)?:~[#$] $/
11
+ clean :escape_codes
17
12
  comment '# '
18
13
 
19
14
  # add a comment in the final conf