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
@@ -3,7 +3,7 @@ class H3C < Oxidized::Model
3
3
 
4
4
  # H3C
5
5
 
6
- prompt /^.*([<\[][\w.-]+[>\]])$/
6
+ prompt /^[<\[][\w.-]+[>\]]$/
7
7
  comment '# '
8
8
 
9
9
  cmd :secret do |cfg|
@@ -54,6 +54,13 @@ class IOS < Oxidized::Model
54
54
  cfg
55
55
  end
56
56
 
57
+ cmd :significant_changes do |cfg|
58
+ cfg.reject_lines [
59
+ /^! (Last|No) configuration change (at|since)/,
60
+ '! NVRAM config last updated at'
61
+ ]
62
+ end
63
+
57
64
  cmd 'show version' do |cfg|
58
65
  comments = []
59
66
  comments << cfg.lines.first
@@ -105,6 +112,8 @@ class IOS < Oxidized::Model
105
112
  comments << "CPU:#{slave} #{cpu}#{cpuxtra}#{slaveslot}"
106
113
  end
107
114
 
115
+ comments << line.chomp if line.start_with?('Motherboard')
116
+
108
117
  comments << "Image: #{Regexp.last_match(1)}" if line =~ /^System image file is "([^"]*)"$/
109
118
  end
110
119
  comments << "\n"
@@ -126,12 +135,17 @@ class IOS < Oxidized::Model
126
135
  cmd_line = 'show running-config'
127
136
  cmd_line += ' view full' if vars(:ios_rbac)
128
137
  cmd cmd_line do |cfg|
129
- cfg = cfg.each_line.to_a[3..-1]
130
- cfg = cfg.reject { |line| line.match /^ntp clock-period / }.join
131
- cfg = cfg.each_line.reject do |line|
132
- line.match /^! (Last|No) configuration change (at|since).*/ unless line =~ /\d+\sby\s\S+$/
133
- end.join
134
- cfg.gsub! /^Current configuration : [^\n]*\n/, ''
138
+ cfg = cfg.cut_head(3)
139
+ cfg = cfg.reject_lines [
140
+ /^ntp clock-period /,
141
+ /^Current configuration : \S+/
142
+ ]
143
+ unless vars("output_store_mode") == "on_significant"
144
+ cfg = cfg.reject_lines [
145
+ # Only store the line "configuration change" when a user is specified
146
+ /^! (Last|No) configuration change (at|since)(?!.*\d+ by \S+$)/
147
+ ]
148
+ end
135
149
  cfg.gsub! /^ tunnel mpls traffic-eng bandwidth[^\n]*\n*(
136
150
  (?: [^\n]*\n*)*
137
151
  tunnel mpls traffic-eng auto-bw)/mx, '\1'
@@ -147,17 +161,11 @@ class IOS < Oxidized::Model
147
161
  end
148
162
 
149
163
  cfg :telnet, :ssh do
150
- # preferred way to handle additional passwords
151
- post_login do
152
- if vars(:enable) == true
153
- cmd "enable"
154
- elsif vars(:enable)
155
- cmd "enable", /^[pP]assword:/
156
- cmd vars(:enable)
157
- end
158
- end
159
164
  post_login 'terminal length 0'
160
165
  post_login 'terminal width 0'
161
166
  pre_logout 'exit'
162
167
  end
168
+
169
+ # preferred way to handle additional passwords (enable)
170
+ macro :enable
163
171
  end
@@ -22,9 +22,11 @@ class IronWare < Oxidized::Model
22
22
  end
23
23
 
24
24
  cmd 'show version' do |cfg|
25
- cfg.gsub! /(^((.*)[Ss]ystem uptime(.*))$)/, '' # remove unwanted line system uptime
26
- cfg.gsub! /(^((.*)[Tt]he system started at(.*))$)/, ''
27
- cfg.gsub! /[Uu]p\s?[Tt]ime is .*/, ''
25
+ cfg = cfg.reject_lines [
26
+ /(^((.*)[Ss]ystem uptime(.*))$)/,
27
+ /(^((.*)[Tt]he system started at(.*))$)/,
28
+ /[Uu]p\s?[Tt]ime is .*/
29
+ ]
28
30
 
29
31
  comment cfg
30
32
  end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ class Ivanti < Oxidized::Model
6
+ BINARY_CONFIG_PATH = '/api/v1/system/binary-configuration'
7
+ REALM_AUTH_PATH = '/api/v1/realm_auth'
8
+ DEFAULT_REALM = 'Users'
9
+
10
+ cmd BINARY_CONFIG_PATH do |b64|
11
+ b64.to_s.lines.map(&:strip).join
12
+ end
13
+
14
+ cfg :http do
15
+ @secure = true
16
+ @port = 443
17
+
18
+ @username = @node.auth[:username]
19
+ @password = @node.auth[:password]
20
+
21
+ realm = vars(:realm) || DEFAULT_REALM
22
+ payload = { realm: realm }.to_json
23
+
24
+ response_body = post_http(
25
+ REALM_AUTH_PATH,
26
+ payload,
27
+ 'Content-Type' => 'application/json',
28
+ 'Authorization' => basic_auth_header
29
+ )
30
+
31
+ begin
32
+ login_data = JSON.parse(response_body)
33
+ rescue JSON::ParserError => e
34
+ logger.error(
35
+ "Failed to parse realm_auth response: #{e.class}: #{e.message}, body=#{response_body.inspect}"
36
+ )
37
+ raise Oxidized::OxidizedError, 'Ivanti: realm_auth returned invalid JSON'
38
+ end
39
+
40
+ api_key = login_data['api_key']
41
+
42
+ if api_key.nil? || api_key.empty?
43
+ logger.error(
44
+ "Failed to obtain api_key from realm_auth, response=#{response_body.inspect}"
45
+ )
46
+ raise Oxidized::OxidizedError, 'Ivanti: realm_auth did not return valid api_key'
47
+ end
48
+
49
+ @username = api_key
50
+ @password = ''
51
+
52
+ logger.debug "Obtained api_key #{api_key[0, 4]}... (len=#{api_key.length})"
53
+ end
54
+ end
@@ -44,8 +44,8 @@ class JunOS < Oxidized::Model
44
44
 
45
45
  cmd('show chassis hardware') { |cfg| comment cfg }
46
46
  cmd('show system license') do |cfg|
47
- cfg.gsub!(/ fib-scale\s+(\d+)/, ' fib-scale <count>')
48
- cfg.gsub!(/ rib-scale\s+(\d+)/, ' rib-scale <count>')
47
+ cfg.gsub!(/ fib[-\s]scale\s+(\d+)/i, ' fib-scale <count>')
48
+ cfg.gsub!(/ rib[-\s]scale\s+(\d+)/i, ' rib-scale <count>')
49
49
  comment cfg
50
50
  end
51
51
  cmd('show system license keys') { |cfg| comment cfg }
@@ -1,8 +1,9 @@
1
1
  class LinuxGeneric < Oxidized::Model
2
2
  using Refinements
3
3
 
4
- prompt /^(\w.*|\W.*)[:#$] /
4
+ prompt /^\S.*[#$] $/
5
5
  comment '# '
6
+ clean :escape_codes
6
7
 
7
8
  # add a comment in the final conf
8
9
  def add_comment(comment)
@@ -53,7 +54,7 @@ class LinuxGeneric < Oxidized::Model
53
54
  end
54
55
 
55
56
  cfg :telnet do
56
- username /^Username:/
57
+ username /(Username|[Ll]ogin):/
57
58
  password /^Password:/
58
59
  end
59
60
 
@@ -63,6 +64,7 @@ class LinuxGeneric < Oxidized::Model
63
64
  cmd "sudo su -", /^\[sudo\] password/
64
65
  cmd @node.auth[:password]
65
66
  elsif vars(:enable)
67
+ # This will only work without localisation (de: "Passwort:", fr: "Mot de passe :"...)
66
68
  cmd "su -", /^Password:/
67
69
  cmd vars(:enable)
68
70
  end
@@ -0,0 +1,60 @@
1
+ module Oxidized
2
+ class Model
3
+ module Macros
4
+ using Refinements
5
+ VERBS = %i[
6
+ macro
7
+ clean
8
+ ].freeze
9
+
10
+ VERBS.each do |verb|
11
+ define_method(verb) do |name, *args, **kwargs, &block|
12
+ send("#{verb}_#{name}", *args, **kwargs, &block)
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def macro_enable(regex: /password/i, inputs: %i[telnet ssh], command: "enable")
19
+ inputs = [inputs].flatten.map(&:to_sym)
20
+ cfg(*inputs) do
21
+ post_login do
22
+ if vars(:enable) == true
23
+ cmd command
24
+ elsif vars(:enable)
25
+ cmd command, regex
26
+ cmd vars(:enable)
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ def clean_escape_codes
33
+ ansi_escape_regex = /
34
+ \r? # Optional carriage return at start
35
+ \e # ESC character - starts escape sequence
36
+ (?: # Non-capturing group for different sequence types:
37
+ # Type 1: CSI (Control Sequence Introducer)
38
+ \[ # Literal '[' - starts CSI sequence
39
+ [0-?]* # Parameter bytes: digits (0-9), semicolon, colon, etc.
40
+ [ -\/]* # Intermediate bytes: space through slash characters
41
+ [@-~] # Final byte: determines the actual command
42
+ | # OR
43
+ # Type 2: Simple escape
44
+ [=>] # Single character commands after ESC
45
+ )
46
+ \r? # Optional carriage return at end
47
+ /x
48
+ expect ansi_escape_regex do |data, re|
49
+ data.gsub re, ''
50
+ end
51
+ end
52
+
53
+ def clean_cut(head: 1, tail: 1)
54
+ cmd :all do |cfg|
55
+ cfg.cut_both(head, tail)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -15,10 +15,6 @@ class MLNXOS < Oxidized::Model
15
15
 
16
16
  cmd :all do |cfg|
17
17
  cfg.gsub! /.\x08/, '' # Remove Backspace char
18
- cfg.gsub! /^CPU load averages:\s.+/, '' # Omit constantly changing CPU info
19
- cfg.gsub! /^System memory:\s.+/, '' # Omit constantly changing memory info
20
- cfg.gsub! /^Uptime:\s.+/, '' # Omit constantly changing uptime info
21
- cfg.gsub! /.+Generated at\s\d+.+/, '' # Omit constantly changing generation time info
22
18
  cfg.lines.to_a[2..-3].join
23
19
  end
24
20
 
@@ -29,17 +25,25 @@ class MLNXOS < Oxidized::Model
29
25
  end
30
26
 
31
27
  cmd 'show version' do |cfg|
32
- comment cfg
28
+ cfg = cfg.reject_lines [
29
+ /^CPU load averages:\s.+/, # Omit constantly changing CPU info
30
+ /^System memory:\s.+/, # Omit constantly changing memory info
31
+ /^Uptime:\s.+/ # Omit constantly changing uptime info
32
+ ]
33
+ comment cfg + "\n"
33
34
  end
34
35
 
35
36
  cmd 'show inventory' do |cfg|
36
- comment cfg
37
+ comment cfg + "\n"
37
38
  end
38
39
 
39
40
  cmd 'enable'
40
41
 
41
42
  cmd 'show running-config' do |cfg|
42
- cfg
43
+ cfg.reject_lines [
44
+ # Omit constantly changing generation time info
45
+ /.+Generated at\s\d+.+/
46
+ ]
43
47
  end
44
48
 
45
49
  cfg :ssh do
@@ -1,5 +1,8 @@
1
1
  require 'strscan'
2
2
  require_relative 'outputs'
3
+ require_relative 'dslsetup'
4
+ require_relative 'dslcommands'
5
+ require_relative 'macros'
3
6
 
4
7
  module Oxidized
5
8
  class Model
@@ -7,6 +10,11 @@ module Oxidized
7
10
 
8
11
  using Refinements
9
12
 
13
+ # Domain Specific Language for models
14
+ extend Oxidized::Model::DSLSetup
15
+ extend Oxidized::Model::DSLCommands
16
+ extend Oxidized::Model::Macros
17
+
10
18
  include Oxidized::Config::Vars
11
19
 
12
20
  # rubocop:disable Style/FormatStringToken
@@ -25,6 +33,8 @@ module Oxidized
25
33
  klass.instance_variable_set '@comment', nil
26
34
  klass.instance_variable_set '@prompt', nil
27
35
  klass.instance_variable_set '@metadata', {}
36
+ klass.instance_variable_set '@inputs', nil
37
+
28
38
  else # we're subclassing some existing model, take its variables
29
39
  instance_variables.each do |var|
30
40
  iv = instance_variable_get(var)
@@ -33,136 +43,20 @@ module Oxidized
33
43
  end
34
44
  end
35
45
  end
36
-
37
- def comment(str = "# ")
38
- @comment = if block_given?
39
- yield
40
- elsif not @comment
41
- str
42
- else
43
- @comment
44
- end
45
- end
46
-
47
- def prompt(regex = nil)
48
- @prompt = regex || @prompt
49
- end
50
-
51
- def cfg(*methods, **args, &block)
52
- [methods].flatten.each do |method|
53
- process_args_block(@cfg[method.to_s], args, block)
54
- end
55
- end
56
-
57
- def cfgs
58
- @cfg
59
- end
60
-
61
- def cmd(cmd_arg = nil, **args, &block)
62
- if cmd_arg.instance_of?(Symbol)
63
- process_args_block(@cmd[cmd_arg], args, block)
64
- else
65
- # Normal command
66
- process_args_block(@cmd[:cmd], args,
67
- { cmd: cmd_arg, args: args, block: block })
68
- end
69
- logger.debug "Added #{cmd_arg} to the commands list"
70
- end
71
-
72
- def metadata(position, value = nil, &block)
73
- return unless %i[top bottom].include? position
74
-
75
- if block_given?
76
- @metadata[position] = block
77
- else
78
- @metadata[position] = value
79
- end
80
- end
81
-
82
- def cmds
83
- @cmd
84
- end
85
-
86
- def expect(regex, **args, &block)
87
- process_args_block(@expect, args, [regex, block])
88
- end
89
-
90
- def expects
91
- @expect
92
- end
93
-
94
- def clean(what)
95
- case what
96
- when :escape_codes
97
- ansi_escape_regex = /
98
- \r? # Optional carriage return at start
99
- \e # ESC character - starts escape sequence
100
- (?: # Non-capturing group for different sequence types:
101
- # Type 1: CSI (Control Sequence Introducer)
102
- \[ # Literal '[' - starts CSI sequence
103
- [0-?]* # Parameter bytes: digits (0-9), semicolon, colon, etc.
104
- [ -\/]* # Intermediate bytes: space through slash characters
105
- [@-~] # Final byte: determines the actual command
106
- | # OR
107
- # Type 2: Simple escape
108
- [=>] # Single character commands after ESC
109
- )
110
- \r? # Optional carriage return at end
111
- /x
112
- expect ansi_escape_regex do |data, re|
113
- data.gsub re, ''
114
- end
115
- end
116
- end
117
-
118
- # calls the block at the end of the model, prepending the output of the
119
- # block to the output string
120
- #
121
- # @yield expects block which should return [String]
122
- # @return [void]
123
- def pre(**args, &block)
124
- process_args_block(@procs[:pre], args, block)
125
- end
126
-
127
- # calls the block at the end of the model, adding the output of the block
128
- # to the output string
129
- #
130
- # @yield expects block which should return [String]
131
- # @return [void]
132
- def post(**args, &block)
133
- process_args_block(@procs[:post], args, block)
134
- end
135
-
136
- # @author Saku Ytti <saku@ytti.fi>
137
- # @since 0.0.39
138
- # @return [Hash] hash proc procs :pre+:post to be prepended/postfixed to output
139
- attr_reader :procs
140
-
141
- private
142
-
143
- def process_args_block(target, args, block)
144
- if args[:clear]
145
- if block.instance_of?(Array)
146
- target.reject! { |k, _| k == block[0] }
147
- target.push(block)
148
- elsif block.instance_of?(Hash)
149
- target.reject! { |item| item[:cmd] == block[:cmd] }
150
- target.push(block)
151
- else
152
- target.replace([block])
153
- end
154
- else
155
- method = args[:prepend] ? :unshift : :push
156
- target.send(method, block)
157
- end
158
- end
159
46
  end
160
47
 
161
48
  attr_accessor :input, :node
162
49
 
163
- def cmd(string, &block)
50
+ # input specifies to run this command only with this input type
51
+ # if input is not specified, always run the command
52
+ def cmd(string, input: nil, &block)
164
53
  logger.debug "Executing #{string}"
165
- out = @input.cmd(string)
54
+ out = if input.nil? || input.include?(@input.to_sym)
55
+ out = @input.cmd(string)
56
+ else
57
+ # Do not run this command
58
+ return ''
59
+ end
166
60
  return false unless out
167
61
 
168
62
  out = out.b unless Oxidized.config.input.utf8_encoded?
@@ -245,6 +139,7 @@ module Oxidized
245
139
  data
246
140
  end
247
141
 
142
+ # Get the commands from the model
248
143
  def get
249
144
  logger.debug 'Collecting commands\' outputs'
250
145
  outputs = Outputs.new
@@ -255,7 +150,7 @@ module Oxidized
255
150
 
256
151
  next if args.include?(:if) && !instance_exec(&args[:if])
257
152
 
258
- out = cmd command, &block
153
+ out = cmd command, input: args[:input], &block
259
154
  return false unless out
260
155
 
261
156
  outputs << out
@@ -306,6 +201,13 @@ module Oxidized
306
201
  @input.class.to_s.match(/Telnet/) || vars(:ssh_no_exec)
307
202
  end
308
203
 
204
+ def significant_changes(config)
205
+ self.class.cmds[:significant_changes].each do |block|
206
+ config = instance_exec config, &block
207
+ end
208
+ config
209
+ end
210
+
309
211
  private
310
212
 
311
213
  def process_cmd_output(output, name)
@@ -22,4 +22,10 @@ class NDMS < Oxidized::Model
22
22
  password /^Password:/
23
23
  pre_logout 'exit'
24
24
  end
25
+
26
+ cmd :significant_changes do |cfg|
27
+ cfg.reject_lines [
28
+ 'Last change:'
29
+ ]
30
+ end
25
31
  end
@@ -6,7 +6,7 @@ class Netgear < Oxidized::Model
6
6
  clean :escape_codes
7
7
 
8
8
  # Handle pager for "show version" on old Netgear models: #2394
9
- expect /^--More-- or \(q\)uit$/ do |data, re|
9
+ expect /^--More--(?: or \(q\)uit)?$/ do |data, re|
10
10
  send ' '
11
11
  data.sub re, ''
12
12
  end
@@ -20,7 +20,7 @@ class Netgear < Oxidized::Model
20
20
  end
21
21
 
22
22
  cfg :telnet do
23
- username /^(User:|Applying Interface configuration, please wait ...)/
23
+ username /^(Username:|User:|Applying Interface configuration, please wait ...)/
24
24
  password /^Password:/i
25
25
  end
26
26
 
@@ -58,9 +58,11 @@ class Netgear < Oxidized::Model
58
58
  comment cfg
59
59
  end
60
60
  cmd 'show running-config' do |cfg|
61
- cfg.gsub! /(System Up Time\s+).*/, '\\1 <removed>'
61
+ cfg.gsub! /(System Up Time:?\s+).*/, '\\1 <removed>'
62
62
  cfg.gsub! /(Current SNTP Synchronized Time:).*/, '\\1 <removed>'
63
63
  cfg.gsub! /(Current System Time:).*/, '\\1 <removed>'
64
+ # Remove standalone backspace lines
65
+ cfg.gsub!(/(?:\r?\n)?\x08(?:\r?\n)?/, "\n")
64
66
  cfg
65
67
  end
66
68
  end
@@ -10,7 +10,10 @@ class NXOS < Oxidized::Model
10
10
 
11
11
  cmd :secret do |cfg|
12
12
  cfg.gsub! /^(snmp-server community).*/, '\\1 <secret hidden>'
13
- cfg.gsub! /^(snmp-server user (\S+) (\S+) auth (\S+)) (\S+) (priv) (\S+)/, '\\1 <secret hidden> '
13
+ cfg.gsub!(
14
+ /^(snmp-server user \S+ \S+ auth \S+) \S+ (priv \S+) \S+ /,
15
+ '\\1 <secret hidden> \\2 <secret hidden> '
16
+ )
14
17
  cfg.gsub! /^(snmp-server host.*? )\S+( udp-port \d+)?$/, '\\1<secret hidden>\\2'
15
18
  cfg.gsub! /^(snmp-server mib community-map) \S+ ?(.*)/, '\\1 <secret hidden> \\2'
16
19
  cfg.gsub! /(password \d+) (\S+)/, '\\1 <secret hidden>'
@@ -25,8 +28,8 @@ class NXOS < Oxidized::Model
25
28
  end
26
29
 
27
30
  cmd 'show inventory all' do |cfg|
28
- if cfg.include? "% Invalid parameter detected at '^' marker."
29
- # 'show inventory all' isn't supported on older versions (See Issue #3657)
31
+ if cfg.match? /^% Invalid .* at '\^' marker\./
32
+ # 'show inventory all' isn't supported on older versions (See Issues #3657, #3779)
30
33
  cfg = cmd 'show inventory'
31
34
  end
32
35
  comment cfg
@@ -31,6 +31,11 @@ module Oxidized
31
31
  @outputs.map { |out| out.type }.uniq.compact
32
32
  end
33
33
 
34
+ def merge!(other)
35
+ other.all.each { |o| @outputs << o }
36
+ self
37
+ end
38
+
34
39
  private
35
40
 
36
41
  def initialize
@@ -5,7 +5,8 @@ class Perle < Oxidized::Model
5
5
  comment '! '
6
6
 
7
7
  cmd :all do |cfg|
8
- cfg.cut_both
8
+ cfg = cfg.cut_both
9
+ cfg.delete "\r"
9
10
  end
10
11
 
11
12
  cmd 'show version verbose' do |cfg|
@@ -17,17 +18,22 @@ class Perle < Oxidized::Model
17
18
  end
18
19
 
19
20
  cmd 'show interfaces transceiver' do |cfg|
20
- out = []
21
- cfg.each_line do |line|
22
- out << line if line =~ /SFP Information/
23
- out << line if line =~ /Vendor Name/
24
- out << line if line =~ /Vendor Serial Number/
25
- end
26
- comment out.join + "\n"
21
+ cfg = cfg.keep_lines [
22
+ 'SFP Information',
23
+ 'Vendor Name',
24
+ 'Vendor Serial Number'
25
+ ]
26
+ comment cfg + "\n"
27
27
  end
28
28
 
29
29
  cmd 'show running-config'
30
30
 
31
+ cmd :significant_changes do |cfg|
32
+ cfg.reject_lines [
33
+ /^tacacs-server key 7 \$0\$\S+==$/
34
+ ]
35
+ end
36
+
31
37
  cfg :ssh do
32
38
  post_login 'terminal length 0'
33
39
  pre_logout 'exit'
@@ -29,6 +29,10 @@ class RouterOS < Oxidized::Model
29
29
  comment cfg
30
30
  end
31
31
 
32
+ cmd :significant_changes do |cfg|
33
+ cfg.gsub(/^(#\s+installed-version: [^\n]+\n).*?^(?=# software id)/m, '\1')
34
+ end
35
+
32
36
  post do
33
37
  logger.debug "Running /export for routeros version #{@ros_version}"
34
38
  run_cmd = if vars(:remove_secret)
@@ -0,0 +1,48 @@
1
+ class SmartByte < Oxidized::Model
2
+ using Refinements
3
+
4
+ comment '! '
5
+
6
+ cmd :secret do |cfg|
7
+ cfg.gsub!(/group (\S+) v2c/, 'group <hidden> v2c')
8
+ cfg.gsub!(/community (\S+)/, 'community <hidden>')
9
+ cfg
10
+ end
11
+
12
+ cmd :all do |cfg|
13
+ cfg = cfg.delete("\r")
14
+ cfg.cut_tail
15
+ end
16
+
17
+ cmd 'show running-config'
18
+
19
+ cmd 'show version' do |cfg|
20
+ comment cfg
21
+ end
22
+
23
+ cmd 'show interface optical-transceiver info' do |cfg|
24
+ cfg.gsub! /^\|Transceiver current alarm information: \|\s+\+-------------------------------------------------\+.*?\s+\+-------------------------------------------------\+\s+/m, ''
25
+ comment cfg
26
+ end
27
+
28
+ cmd 'show power' do |cfg|
29
+ comment cfg
30
+ end
31
+
32
+ cfg :telnet do
33
+ username /^.*? login:/
34
+ password /^Password:/
35
+ end
36
+
37
+ cfg :telnet, :ssh do
38
+ post_login do
39
+ if vars(:enable)
40
+ cmd "enable", /^[pP]assword:/
41
+ cmd vars(:enable)
42
+ end
43
+ end
44
+
45
+ post_login 'terminal length 0'
46
+ pre_logout 'exit'
47
+ end
48
+ end