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
@@ -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|
@@ -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
 
@@ -5,9 +5,9 @@ class Cumulus < Oxidized::Model
5
5
  # ^ : match begin of line, to have the most specific prompt
6
6
  # [\w.-]+@[\w.-]+ : user@hostname
7
7
  # (:mgmt)? : optional when logged in out of band
8
- # :~[#$] $ : end of prompt, containing the linux path,
9
- # which is always "~" in our context
10
- prompt /^[\w.-]+@[\w.-]+(:mgmt)?:~[#$] $/
8
+ # :?~[#$] ?$ : end of prompt, containing the linux path,
9
+ # which is always "~" in our context.
10
+ prompt /^[\w.-]+@[\w.-]+(:mgmt)?:?~[#$] ?$/
11
11
  clean :escape_codes
12
12
  comment '# '
13
13
 
@@ -0,0 +1,26 @@
1
+ class Defacto < Oxidized::Model
2
+ prompt /^(\r*[\w.:@()\/_-]+[#>]\s?)$/
3
+ comment '! '
4
+
5
+ clean :cut
6
+
7
+ post do
8
+ cmd "show running-config" do |cfg|
9
+ process_config cfg
10
+ end
11
+ end
12
+
13
+ cfg :telnet do
14
+ username /^(user ?name|login|user)/i
15
+ password /^password/i
16
+ end
17
+
18
+ cfg :telnet, :ssh do
19
+ post_login 'terminal length 0'
20
+ post_login 'terminal width 0'
21
+ pre_logout 'exit'
22
+ pre_logout 'logout'
23
+ end
24
+
25
+ def process_config(cfg) = cfg
26
+ end
@@ -8,6 +8,7 @@ class DlinkNextGen < Oxidized::Model
8
8
  comment '# '
9
9
 
10
10
  cmd :all do |cfg|
11
+ cfg.gsub!("\0", "") # Remove NULL bytes that cause Git to detect the file as binary
11
12
  cfg.each_line.to_a[2..-2].map { |line| line.delete("\r").rstrip }.join("\n") + "\n"
12
13
  end
13
14
 
@@ -0,0 +1,93 @@
1
+ module Oxidized
2
+ class Model
3
+ # Domain Specific Language for model commands
4
+ module DSLCommands
5
+ # Store a command to be run against the device
6
+ # cmd_arg can be:
7
+ # - a string (the command to be run)
8
+ # - a symbol:
9
+ # - :all - run the block against each command output
10
+ # - :secret - run the block against each command output when
11
+ # vars :remove_secret is true
12
+ # - :significant_changes - use the block to remove unsignificant
13
+ # changes
14
+ # Optional arguments (**args):
15
+ # - clear: true
16
+ # replace all the stored blocks for this command (monkey patching)
17
+ # - prepend: true
18
+ # prepend the block to the stored blocks for this command (monkey
19
+ # patching)
20
+ # - if: lambda
21
+ # run the command only if the lambda evaluates to true
22
+ # - input: symbol or array of symbols
23
+ # for the inputs this command is to run against (default - run
24
+ # every command)
25
+ def cmd(cmd_arg = nil, **args, &block)
26
+ if cmd_arg.instance_of?(Symbol)
27
+ process_args_block(@cmd[cmd_arg], args, block)
28
+ else
29
+ return unless valid_cmd_args?(cmd_arg, args)
30
+
31
+ # Always use an array for :input
32
+ args[:input] = Array(args[:input]) if args.include?(:input)
33
+ process_args_block(@cmd[:cmd], args,
34
+ { cmd: cmd_arg, args: args, block: block })
35
+ end
36
+ logger.debug "Added #{cmd_arg} to the commands list"
37
+ end
38
+
39
+ def cmds
40
+ @cmd
41
+ end
42
+
43
+ # calls the block at the end of the model, prepending the output of the
44
+ # block to the output string
45
+ def pre(**args, &block)
46
+ process_args_block(@procs[:pre], args, block)
47
+ end
48
+
49
+ # calls the block at the end of the model, adding the output of the block
50
+ # to the output string
51
+ def post(**args, &block)
52
+ process_args_block(@procs[:post], args, block)
53
+ end
54
+
55
+ # @procs is a hash of procs (:pre+:post) to be prepended/postfixed to
56
+ # output
57
+ attr_reader :procs
58
+
59
+ private
60
+
61
+ def process_args_block(target, args, block)
62
+ if args[:clear]
63
+ if block.instance_of?(Array)
64
+ target.reject! { |k, _| k == block[0] }
65
+ target.push(block)
66
+ elsif block.instance_of?(Hash)
67
+ target.reject! { |item| item[:cmd] == block[:cmd] }
68
+ target.push(block)
69
+ else
70
+ target.replace([block])
71
+ end
72
+ else
73
+ method = args[:prepend] ? :unshift : :push
74
+ target.send(method, block)
75
+ end
76
+ end
77
+
78
+ def valid_cmd_args?(cmd_arg, args)
79
+ if args.include?(:if) && !(args[:if].is_a?(Proc) && args[:if].lambda?)
80
+ logger.error "cmd #{cmd_arg.dump}: if must be a lambda"
81
+ return false
82
+ end
83
+
84
+ if args.include?(:input) && ![Symbol, Array].include?(args[:input].class)
85
+ logger.error "cmd #{cmd_arg.dump}: input must be a symbol or an array of symbols"
86
+ return false
87
+ end
88
+
89
+ true
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,102 @@
1
+ module Oxidized
2
+ class Model
3
+ # Domain Specific Language for model setup
4
+ module DSLSetup
5
+ def prompt(regex = nil)
6
+ @prompt = regex || @prompt
7
+ end
8
+
9
+ def comment(str = "# ")
10
+ @comment = if block_given?
11
+ yield
12
+ elsif not @comment
13
+ str
14
+ else
15
+ @comment
16
+ end
17
+ end
18
+
19
+ def expect(regex, **args, &block)
20
+ process_args_block(@expect, args, [regex, block])
21
+ end
22
+
23
+ def expects
24
+ @expect
25
+ end
26
+
27
+ def cfg(*methods, **args, &block)
28
+ [methods].flatten.each do |method|
29
+ process_args_block(@cfg[method.to_s], args, block)
30
+ end
31
+ end
32
+
33
+ def cfgs
34
+ @cfg
35
+ end
36
+
37
+ # Define multiple inputs as a sequence of equivalent options
38
+ # Example: use ssh or telnet, then scp or ftp:
39
+ # [:ssh, [:scp, :ftp]]
40
+ def inputs(list = nil)
41
+ return @inputs if list.nil?
42
+
43
+ validate_inputs(list)
44
+ @inputs = list
45
+ end
46
+
47
+ # Returns the input sequence for the model as an array of arrays of input
48
+ # classes, filtered and ordered according to the provided +input_classes+
49
+ # (as specified in the oxidized configuration file).
50
+ def input_sequence(input_classes)
51
+ model_inputs = inputs || [
52
+ @cfg.filter_map do |input, block_list|
53
+ input.to_sym unless block_list.empty?
54
+ end
55
+ ]
56
+
57
+ model_inputs.map do |sequence|
58
+ sequence = [sequence] unless sequence.is_a? Array
59
+ selected = input_classes.select { |input| sequence.include?(input.to_sym) }
60
+ logger.error "Needs one of #{sequence.inspect} to be configured" if selected.empty?
61
+
62
+ selected
63
+ end
64
+ end
65
+
66
+ def metadata(position, value = nil, &block)
67
+ return unless %i[top bottom].include? position
68
+
69
+ if block_given?
70
+ @metadata[position] = block
71
+ else
72
+ @metadata[position] = value
73
+ end
74
+ end
75
+
76
+ private
77
+
78
+ def validate_inputs(list)
79
+ message = "inputs must be an array containing symbols or " \
80
+ "arrays of symbols"
81
+
82
+ raise ArgumentError, message unless list.is_a? Array
83
+ raise ArgumentError, message if list.empty?
84
+
85
+ list.each do |group|
86
+ case group
87
+ when Symbol
88
+ # Everything is fine
89
+ when Array
90
+ raise ArgumentError, message if group.empty?
91
+
92
+ group.each do |input|
93
+ raise ArgumentError, message unless input.is_a? Symbol
94
+ end
95
+ else
96
+ raise ArgumentError, message
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -19,11 +19,11 @@ class EFOS < Oxidized::Model
19
19
  end
20
20
 
21
21
  cmd 'show running-config' do |cfg|
22
- cfg.each_line
23
- .reject { |line| line.match(/System Up Time/) }
24
- .reject { |line| line.match(/Current System Time:/) }
25
- .reject { |line| line.match(/Current SNTP Synchronized Time:/) }
26
- .join
22
+ cfg.reject_lines [
23
+ 'System Up Time',
24
+ 'Current System Time:',
25
+ 'Current SNTP Synchronized Time:'
26
+ ]
27
27
  end
28
28
 
29
29
  cfg :telnet, :ssh do
@@ -0,0 +1,36 @@
1
+ class Exalink < Oxidized::Model
2
+ using Refinements
3
+
4
+ prompt /^([\w.@()-]+[#>]\s?)$/
5
+ comment '! '
6
+
7
+ cmd :all do |cfg|
8
+ cfg.gsub! /\r\n?/, "\n"
9
+ cfg.cut_both
10
+ end
11
+
12
+ cmd 'show version' do |cfg|
13
+ comment cfg.reject_lines /uptime/i
14
+ end
15
+
16
+ cmd 'show port' do |cfg|
17
+ comment cfg
18
+ end
19
+
20
+ cmd 'show running-config' do |cfg|
21
+ cfg.gsub! /^(show run.*)$/, '! \1'
22
+ cfg.gsub! /^!Time:[^\n]*\n/, ''
23
+ cfg
24
+ end
25
+
26
+ cfg :telnet do
27
+ username /^login:/
28
+ password /^Password:/
29
+ end
30
+
31
+ cfg :ssh, :telnet do
32
+ post_login 'terminal length 0'
33
+ post_login 'terminal width 0'
34
+ pre_logout 'logout'
35
+ end
36
+ end
@@ -1,7 +1,7 @@
1
1
  class FastIron < Oxidized::Model
2
2
  using Refinements
3
3
 
4
- prompt /^([\w.@()-]+[#>]\s?)$/
4
+ prompt /^([\w.@\/()-]+[#>]\s?)$/
5
5
  comment '! '
6
6
 
7
7
  cmd :all do |cfg|
@@ -43,7 +43,7 @@ class FastIron < Oxidized::Model
43
43
  comment cfg
44
44
  end
45
45
 
46
- cmd 'show stack' do |cfg|
46
+ cmd 'show stack | exclude T=' do |cfg|
47
47
  comment cfg
48
48
  end
49
49
 
@@ -31,9 +31,7 @@ class FireLinuxOS < Oxidized::Model
31
31
  end
32
32
 
33
33
  cmd 'show version system' do |cfg|
34
- cfg = cfg.each_line.reject { |line| line.match /(\s+up\s+\d+\s+)|(.*days.*)/ }
35
- cfg = cfg.join
36
- comment cfg
34
+ comment cfg.reject_lines [/(\s+up\s+\d+\s+)|(.*days.*)/]
37
35
  end
38
36
 
39
37
  cmd 'show inventory' do |cfg|
@@ -0,0 +1,160 @@
1
+ class FortiGate < Oxidized::Model
2
+ using Refinements
3
+
4
+ comment '# '
5
+
6
+ prompt /^(\(\w\) )?([-\w.~]+(\s[(\w\-.)]+)?~?\s?[#>$]\s?)$/
7
+
8
+ # When a post-login-banner is enabled, you have to press "a" to log in
9
+ expect /^\(Press\s'a'\sto\saccept\):/ do |data, re|
10
+ send 'a'
11
+ data.sub re, ''
12
+ end
13
+
14
+ expect /^--More--\s$/ do |data, re|
15
+ send ' '
16
+ data.sub re, ''
17
+ end
18
+
19
+ cmd :all do |cfg|
20
+ # Remove junk after --More-- pager
21
+ cfg = cfg.gsub(/\r +\r/, '')
22
+ # Remove \r\n after command echo
23
+ cfg = cfg.gsub("\r\n", "\n")
24
+ # remove command echo and prompt
25
+ cfg.cut_both
26
+ end
27
+
28
+ cmd :secret do |cfg|
29
+ # Remove private key for encrypted configs
30
+ cfg.gsub! /^(\#private-encryption-key=).+/, '\\1 <configuration removed>'
31
+ # ENC indicates an encrypted password, and secret indicates a secret string
32
+ cfg.gsub! /(set .+ ENC) .+/, '\\1 <configuration removed>'
33
+ cfg.gsub! /(set .*secret) .+/, '\\1 <configuration removed>'
34
+ # A number of other statements also contains sensitive strings
35
+ cfg.gsub! /(set (?:passwd|password|key|group-password|auth-password-l1|auth-password-l2|rsso|history0|history1)) .+/, '\\1 <configuration removed>'
36
+ cfg.gsub! /(set md5-key [0-9]+) .+/, '\\1 <configuration removed>'
37
+ cfg.gsub! /(set private-key ).*?-+END (ENCRYPTED|RSA|OPENSSH) PRIVATE KEY-+\n?"$/m, '\\1<configuration removed>'
38
+ cfg.gsub! /(set privatekey ).*?-+END (ENCRYPTED|RSA|OPENSSH) PRIVATE KEY-+\n?"$/m, '\\1<configuration removed>'
39
+ cfg.gsub! /(set ca )"-+BEGIN.*?-+END CERTIFICATE-+"$/m, '\\1<configuration removed>'
40
+ cfg.gsub! /(set csr ).*?-+END CERTIFICATE REQUEST-+"$/m, '\\1<configuration removed>'
41
+ cfg
42
+ end
43
+
44
+ cmd 'get system status' do |cfg|
45
+ @vdom_enabled = cfg.match(/^Virtual domain configuration: (enable|multiple)/)
46
+ @ha_cluster = cfg.match(/^Current HA mode: a-/) # a-p or a-a
47
+ cfg = cfg.keep_lines [
48
+ "Version: ",
49
+ "Security Level: ",
50
+ "Serial-Number: ",
51
+ "BIOS version: ",
52
+ "System Part-Number: ",
53
+ "Hostname: ",
54
+ "Operation Mode: ",
55
+ "Current virtual domain: ",
56
+ "Max number of virtual domains: ",
57
+ "Virtual domains status:",
58
+ "Virtual domain configuration: ",
59
+ "FIPS-CC mode: ",
60
+ "Current HA mode: ",
61
+ "Private Encryption: ",
62
+ # Lines in FortiGate-VM64
63
+ "License Expiration Date: ",
64
+ "License Status: ",
65
+ "VM Resources: "
66
+ ]
67
+ comment cfg + "\n"
68
+ end
69
+
70
+ cmd 'config global', if: -> { @vdom_enabled } do |_cfg|
71
+ ''
72
+ end
73
+
74
+ cmd 'get system ha status', if: -> { @ha_cluster } do |cfg|
75
+ cfg = cfg.keep_lines [
76
+ "HA Health Status:",
77
+ "Model: ",
78
+ "Mode: ",
79
+ "number of member: ",
80
+ /^(Master|Slave|Primary|Secondary): /
81
+ ]
82
+ comment cfg + "\n"
83
+ end
84
+
85
+ cmd 'get hardware status' do |cfg|
86
+ comment cfg
87
+ end
88
+
89
+ cmd "diagnose hardware deviceinfo psu" do |cfg|
90
+ skip_patterns = [
91
+ /Command fail\./, # The device doesn't support this command
92
+ /Power Supply +Status/ # We only get a status, but no serial numbers
93
+ ]
94
+ if skip_patterns.any? { |p| cfg.match?(p) }
95
+ logger.debug "No PSU serial numbers available"
96
+ ''
97
+ else
98
+ comment cfg
99
+ end
100
+ end
101
+
102
+ cmd "get system interface transceiver" do |cfg|
103
+ cfg = cfg.keep_lines [
104
+ /^Interface \w/,
105
+ "Vendor Name",
106
+ "Part No.",
107
+ "Serial No."
108
+ ]
109
+ cfg = cfg.reject_lines ["Transceiver is not detected"]
110
+ comment cfg + "\n"
111
+ end
112
+
113
+ cmd 'diagnose autoupdate version', if: -> { vars(:fortios_autoupdate) || vars(:fortigate_autoupdate) } do |cfg|
114
+ if vars(:fortios_autoupdate)
115
+ logger.warn("The variable fortios_autoupdate is deprecated. Migrate to fortigate_autoupdate")
116
+ end
117
+
118
+ cfg = cfg.sub(/FDS Address\n---------\n.*\n/, '')
119
+ comment cfg.reject_lines ["Last Update", "Result :"]
120
+ end
121
+
122
+ cmd 'end', if: -> { @vdom_enabled } do |_cfg|
123
+ ''
124
+ end
125
+
126
+ def clean_config(cfg)
127
+ cfg = cfg.reject_lines ['#conf_file_ver=']
128
+ cfg.gsub!(/(set comments "Error \(No order found for account ID \d+\) on).*/,
129
+ '\\1 <stripped>')
130
+ cfg
131
+ end
132
+
133
+ # If vars fullconfig is set to true, we get the full config (including default
134
+ # values)
135
+ cmd "show full-configuration | grep .", if: -> { vars(:fullconfig) } do |cfg|
136
+ clean_config cfg
137
+ end
138
+ # else backup as in Fortigate GUI
139
+ cmd "show | grep .", if: -> { !vars(:fullconfig) } do |cfg|
140
+ clean_config cfg
141
+ end
142
+
143
+ cmd :significant_changes do |cfg|
144
+ cfg = cfg.reject_lines [
145
+ /^ +set \S+ ENC \S+$/
146
+ ]
147
+ cfg.gsub!(/(config firewall internet-service-name\n).*?(\nend\n)/m, '')
148
+ cfg.gsub!(/set private-key .*?-+END \S+ PRIVATE KEY-+\n?"$/m, '')
149
+ cfg
150
+ end
151
+
152
+ cfg :telnet do
153
+ username /^[lL]ogin:/
154
+ password /^Password:/
155
+ end
156
+
157
+ cfg :telnet, :ssh do
158
+ pre_logout "exit"
159
+ end
160
+ end
@@ -16,9 +16,13 @@ class FortiOS < Oxidized::Model
16
16
  data.sub re, ''
17
17
  end
18
18
 
19
- cmd :all do |cfg, cmdstring|
20
- new_cfg = comment "COMMAND: #{cmdstring}\n"
21
- new_cfg << cfg.each_line.to_a[1..-2].map { |line| line.gsub(/(conf_file_ver=)(.*)/, '\1<stripped>\3') }.join
19
+ cmd :all do |cfg|
20
+ # Remove junk after --More-- pager
21
+ cfg = cfg.gsub(/\r +\r/, '')
22
+ # Remove \r\n after command echo
23
+ cfg = cfg.gsub("\r\n", "\n")
24
+ # remove command echo and prompt
25
+ cfg.cut_both
22
26
  end
23
27
 
24
28
  cmd :secret do |cfg|
@@ -38,74 +42,29 @@ class FortiOS < Oxidized::Model
38
42
  end
39
43
 
40
44
  cmd 'get system status' do |cfg|
41
- @vdom_enabled = cfg.match /Virtual domain configuration: (enable|multiple)/
42
- cfg.gsub! /(System time:).*/i, '\\1 <stripped>'
43
- cfg.gsub! /(Cluster (?:uptime|state change time):).*/, '\\1 <stripped>'
44
- cfg.gsub! /(Current Time\s+:\s+)(.*)/, '\1<stripped>'
45
- cfg.gsub! /(Uptime:\s+)(.*)/, '\1<stripped>\3'
46
- cfg.gsub! /(Last reboot:\s+)(.*)/i, '\1<stripped>\3'
47
- cfg.gsub! /(Disk Usage\s+:\s+)(.*)/, '\1<stripped>'
48
- cfg.gsub! /(^\S+ (?:disk|DB):\s+)(.*)/, '\1<stripped>\3'
49
- cfg.gsub! /(VM Registration:\s+)(.*)/, '\1<stripped>\3'
50
- cfg.gsub! /(Virus-DB|Extended DB|FMWP-DB|IPS-DB|IPS-ETDB|APP-DB|INDUSTRIAL-DB|Botnet DB|IPS Malicious URL Database|AV AI\/ML Model|IoT-Detect).*/, '\\1 <db version stripped>'
51
- comment cfg
45
+ cfg = cfg.reject_lines [
46
+ "Current Time",
47
+ "Disk Usage",
48
+ "Release Version Information",
49
+ "Branch Point",
50
+ "Daylight Time Saving",
51
+ "Time Zone",
52
+ "x86-64 Applications",
53
+ "File System",
54
+ "Image Signature"
55
+ ]
56
+ comment cfg + "\n"
52
57
  end
53
58
 
54
- post do
55
- cfg = []
56
- cfg << cmd('config global') if @vdom_enabled
57
-
58
- cfg << cmd('get system ha status') do |cfg_ha|
59
- cfg_ha = cfg_ha.each_line.select { |line| line.match /^(HA Health Status|Mode|Model|Master|Slave|Primary|Secondary|# COMMAND)(\s+)?:/ }.join
60
- comment cfg_ha
61
- end
62
-
63
- cfg << cmd('get hardware status') do |cfg_hw|
64
- comment cfg_hw
65
- end
66
-
67
- # default behaviour: include autoupdate output (backwards compatibility)
68
- # do not include if variable "show_autoupdate" is set to false
69
- if defined?(vars(:fortios_autoupdate)).nil? || vars(:fortios_autoupdate)
70
- cfg << cmd('diagnose autoupdate version') do |cfg_auto|
71
- cfg_auto.gsub! /(FDS Address\n---------\n).*/, '\\1IP Address removed'
72
- comment cfg_auto.each_line.reject { |line| line.match /Last Update|Result/ }.join
73
- end
74
- end
75
-
76
- cfg << cmd('end') if @vdom_enabled
77
-
78
- # Different OS have different commands - we use the first that works
79
- # - For fortigate > 7 and possibly earlier versions, we use:
80
- # show | grep . # backup as in fortigate GUI
81
- # show full-configuration | grep . # bakup including default values
82
- # | grep is used to avoid the --More-- prompt
83
- # - It is not documented which systems need the commands without | grep:
84
- # show full-configuration
85
- # show
86
- # Document it here and make a PR on github if you know!
87
- # By default, we use the configuration without default values
88
- # If fullconfig: true is set in the configuration, we get the full config
89
- commandlist = if vars(:fullconfig)
90
- ['show full-configuration | grep .',
91
- 'show full-configuration', 'show']
92
- else
93
- ['show | grep .',
94
- 'show full-configuration', 'show']
95
- end
96
-
97
- commandlist.each do |fullcmd|
98
- fullcfg = cmd(fullcmd)
99
- # Don't show for unsupported devices (e.g. FortiAnalyzer, FortiManager, FortiMail)
100
- next if fullcfg.lines[1..3].join =~ /(Parsing error at|command parse error)/
101
-
102
- fullcfg.gsub! /(set comments "Error \(No order (found )?for (account )?ID \d+\) on).*/, '\\1 <stripped>"'
103
-
104
- cfg << fullcfg
105
- break
106
- end
59
+ cmd "show" do |cfg|
60
+ cfg.reject_lines ['#config-version=']
61
+ end
107
62
 
108
- cfg.join
63
+ cmd :significant_changes do |cfg|
64
+ cfg = cfg.reject_lines [
65
+ /^ +set \S+ ENC \S+$/
66
+ ]
67
+ cfg.gsub(/set private-key .*?-+END \S+ PRIVATE KEY-+\n?"$/m, '')
109
68
  end
110
69
 
111
70
  cfg :telnet do
@@ -114,6 +73,6 @@ class FortiOS < Oxidized::Model
114
73
  end
115
74
 
116
75
  cfg :telnet, :ssh do
117
- pre_logout "exit\n"
76
+ pre_logout "exit"
118
77
  end
119
78
  end
@@ -22,9 +22,7 @@ class FSOS < Oxidized::Model
22
22
 
23
23
  cmd 'show version' do |cfg|
24
24
  # Remove uptime so the result doesn't change every time
25
- cfg.gsub! /.*uptime is.*\n/, ''
26
- cfg.gsub! /.*System uptime.*\n/, ''
27
- comment cfg
25
+ comment cfg.reject_lines ['uptime is', 'System uptime']
28
26
  end
29
27
 
30
28
  cmd 'show running-config' do |cfg|
@@ -0,0 +1,19 @@
1
+ class GrandstreamHT8xx < Oxidized::Model
2
+ using Refinements
3
+
4
+ # Anchored prompt to avoid matching XML content
5
+ prompt /^(GS|CONFIG)>\s?$/
6
+ comment '# '
7
+
8
+ cfg :ssh do
9
+ # After login go to configuration submenu (looks like enabled in other devices)
10
+ post_login 'config'
11
+ # When logout use double exit - first from configuration submenu, and second to disconnect from device
12
+ pre_logout 'exit'
13
+ pre_logout 'exit'
14
+ end
15
+
16
+ cmd 'export' do |cfg|
17
+ cfg.cut_both
18
+ end
19
+ end