oxidized 0.30.1 → 0.31.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 (113) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +2 -2
  3. data/.github/workflows/stale.yml +4 -2
  4. data/.rubocop.yml +18 -2
  5. data/.rubocop_todo.yml +5 -12
  6. data/CHANGELOG.md +61 -1
  7. data/CONTRIBUTING.md +5 -0
  8. data/Dockerfile +82 -21
  9. data/README.md +5 -21
  10. data/Rakefile +3 -2
  11. data/docs/Configuration.md +36 -12
  12. data/docs/Creating-Models.md +45 -4
  13. data/docs/Hooks.md +34 -0
  14. data/docs/Issues.md +91 -0
  15. data/docs/Model-Notes/Cumulus.md +5 -0
  16. data/docs/Model-Notes/FSOS.md +5 -0
  17. data/docs/Model-Notes/FortiOS.md +21 -5
  18. data/docs/Model-Notes/HPEAruba.md +31 -0
  19. data/docs/Model-Notes/OS6.md +10 -0
  20. data/docs/Model-Notes/RouterOS.md +15 -0
  21. data/docs/Model-Notes/SikluMHTG.md +7 -0
  22. data/docs/Outputs.md +2 -0
  23. data/docs/Release.md +18 -15
  24. data/docs/Sources.md +21 -0
  25. data/docs/Supported-OS-Types.md +11 -5
  26. data/docs/Troubleshooting.md +35 -0
  27. data/examples/device-simulation/README.md +173 -0
  28. data/examples/device-simulation/cmdsets/aoscx +9 -0
  29. data/examples/device-simulation/cmdsets/arubainstant +5 -0
  30. data/examples/device-simulation/cmdsets/asa +7 -0
  31. data/examples/device-simulation/cmdsets/ios +7 -0
  32. data/examples/device-simulation/cmdsets/nxos +5 -0
  33. data/examples/device-simulation/cmdsets/routeros +5 -0
  34. data/examples/device-simulation/cmdsets/srosmd +11 -0
  35. data/examples/device-simulation/device2yaml.rb +225 -0
  36. data/examples/device-simulation/yaml/aoscx_R0X25A-6410_FL.10.10.1100.yaml +2281 -0
  37. data/examples/device-simulation/yaml/aoscx_R8N85A-C6000-48G-CL4_PL.10.08.1010.yaml +451 -0
  38. data/examples/device-simulation/yaml/arubainstant_IAP515_8.10.0.6_VWLC.yaml +213 -0
  39. data/examples/device-simulation/yaml/asa_5512_9.12-4-67_single-context.yaml +531 -0
  40. data/examples/device-simulation/yaml/asr920_16.8.1b.yaml +1122 -0
  41. data/examples/device-simulation/yaml/garderos_R7709_003_006_068.yaml +101 -0
  42. data/examples/device-simulation/yaml/iosxe_C9200L-24P-4G_17.09.04a.yaml +514 -0
  43. data/examples/device-simulation/yaml/iosxe_C9800-L-F-K9_17.06.05.yaml +417 -0
  44. data/examples/device-simulation/yaml/riverbed_915.yaml +123 -0
  45. data/examples/device-simulation/yaml/routeros_CHR_7.10.1.yaml +145 -0
  46. data/examples/device-simulation/yaml/routeros_CHR_7.16.yaml +79 -0
  47. data/examples/device-simulation/yaml/routeros_L009UiGS_7.15.2.yaml +353 -0
  48. data/examples/podman-compose/Makefile +60 -17
  49. data/examples/podman-compose/README.md +63 -27
  50. data/examples/podman-compose/docker-compose.yml +11 -2
  51. data/examples/podman-compose/gitserver/.gitignore +1 -0
  52. data/examples/podman-compose/gitserver/Dockerfile +14 -0
  53. data/examples/podman-compose/model-simulation/Dockerfile-model +1 -1
  54. data/examples/podman-compose/model-simulation/asternos.sh +2 -0
  55. data/examples/podman-compose/oxidized-config/.gitignore +2 -0
  56. data/examples/podman-compose/oxidized-config/config +1 -1
  57. data/examples/podman-compose/oxidized-config/config_csv-file +46 -0
  58. data/examples/podman-compose/oxidized-config/config_csv-gitserver +56 -0
  59. data/examples/podman-compose/oxidized-ssh/.gitignore +1 -0
  60. data/lib/oxidized/config.rb +7 -1
  61. data/lib/oxidized/hook/githubrepo.rb +37 -7
  62. data/lib/oxidized/hook/slackdiff.rb +29 -7
  63. data/lib/oxidized/input/http.rb +1 -0
  64. data/lib/oxidized/input/telnet.rb +1 -1
  65. data/lib/oxidized/manager.rb +17 -16
  66. data/lib/oxidized/model/aoscx.rb +16 -2
  67. data/lib/oxidized/model/aosw.rb +7 -1
  68. data/lib/oxidized/model/arubainstant.rb +90 -0
  69. data/lib/oxidized/model/audiocodes.rb +2 -2
  70. data/lib/oxidized/model/cnos.rb +13 -10
  71. data/lib/oxidized/model/cumulus.rb +3 -0
  72. data/lib/oxidized/model/dlink.rb +1 -0
  73. data/lib/oxidized/model/dlinknextgen.rb +3 -0
  74. data/lib/oxidized/model/edgecos.rb +2 -1
  75. data/lib/oxidized/model/eos.rb +2 -0
  76. data/lib/oxidized/model/f5os.rb +17 -0
  77. data/lib/oxidized/model/firewareos.rb +10 -1
  78. data/lib/oxidized/model/fortios.rb +24 -1
  79. data/lib/oxidized/model/garderos.rb +43 -0
  80. data/lib/oxidized/model/h3c.rb +1 -1
  81. data/lib/oxidized/model/ibos.rb +1 -0
  82. data/lib/oxidized/model/ios.rb +20 -12
  83. data/lib/oxidized/model/iosxr.rb +1 -1
  84. data/lib/oxidized/model/lenovonos.rb +2 -0
  85. data/lib/oxidized/model/linuxgeneric.rb +1 -1
  86. data/lib/oxidized/model/netgear.rb +1 -1
  87. data/lib/oxidized/model/nodegrid.rb +1 -1
  88. data/lib/oxidized/model/nsxdfw.rb +30 -0
  89. data/lib/oxidized/model/nxos.rb +2 -1
  90. data/lib/oxidized/model/os6.rb +48 -0
  91. data/lib/oxidized/model/rgos.rb +1 -1
  92. data/lib/oxidized/model/riverbed.rb +104 -0
  93. data/lib/oxidized/model/routeros.rb +2 -2
  94. data/lib/oxidized/model/saos.rb +18 -1
  95. data/lib/oxidized/model/siklumhtg.rb +22 -0
  96. data/lib/oxidized/model/uplinkolt.rb +46 -0
  97. data/lib/oxidized/model/vyatta.rb +2 -2
  98. data/lib/oxidized/model/xos.rb +7 -0
  99. data/lib/oxidized/node.rb +30 -18
  100. data/lib/oxidized/nodes.rb +13 -5
  101. data/lib/oxidized/output/file.rb +45 -42
  102. data/lib/oxidized/output/git.rb +185 -160
  103. data/lib/oxidized/output/gitcrypt.rb +188 -186
  104. data/lib/oxidized/output/http.rb +53 -51
  105. data/lib/oxidized/output/output.rb +6 -4
  106. data/lib/oxidized/source/csv.rb +44 -49
  107. data/lib/oxidized/source/http.rb +63 -81
  108. data/lib/oxidized/source/jsonfile.rb +63 -0
  109. data/lib/oxidized/source/source.rb +43 -18
  110. data/lib/oxidized/source/sql.rb +66 -59
  111. data/lib/oxidized/version.rb +2 -2
  112. data/oxidized.gemspec +22 -16
  113. metadata +111 -15
@@ -1,8 +1,10 @@
1
1
  services:
2
2
  oxidized:
3
3
  # Choose the image that you want to test
4
- # image: docker.io/oxidized/oxidized:0.29.1
5
- image: docker.io/oxidized/oxidized:latest
4
+ # image: docker.io/oxidized/oxidized:0.30.1
5
+ # image: docker.io/oxidized/oxidized:latest
6
+ # local/oxidized is build by "make oxidized-image" and "make run"
7
+ image: local/oxidized
6
8
  ports:
7
9
  - 127.0.0.1:8042:8888/tcp
8
10
  environment:
@@ -13,9 +15,16 @@ services:
13
15
  volumes:
14
16
  - ./oxidized-config:/home/oxidized/.config/oxidized
15
17
  - ./oxidized-ssh:/home/oxidized/.ssh
18
+
16
19
  # This is a simulated network device for the example to work out of the box
17
20
  asternos-device:
18
21
  image: localhost/local/model
19
22
  volumes:
20
23
  - ./model-simulation/asternos.sh:/home/oxidized/.profile
21
24
  - ./model-simulation/asternos.sh:/home/admin/.profile
25
+
26
+ # This is a gitserver to push our configs
27
+ gitserver:
28
+ image: localhost/local/gitserver
29
+ volumes:
30
+ - ./gitserver/repo.git:/home/git/repo.git
@@ -0,0 +1 @@
1
+ repo.git
@@ -0,0 +1,14 @@
1
+ FROM docker.io/phusion/baseimage:noble-1.0.0
2
+
3
+ # Use baseimage-docker's init system.
4
+ CMD ["/sbin/my_init"]
5
+
6
+ # enable ssh
7
+ RUN rm -f /etc/service/sshd/down
8
+ RUN /etc/my_init.d/00_regen_ssh_host_keys.sh
9
+
10
+ # Add user for the gitserver. The password is "git"
11
+ RUN useradd -m git -p '$6$32WDb0LTFyQkLffy$u15COVx7CQ4tgp4JT4DO4LJ96q/jwFSpuZC3WrllNQDNa6nW1LhJKW9rLV57ak3rj9Ln./aRA85jzeof1B0Gi1' -s /bin/bash -u 30001
12
+
13
+ # And install git
14
+ RUN install_clean git
@@ -1,4 +1,4 @@
1
- FROM docker.io/phusion/baseimage:jammy-1.0.2
1
+ FROM docker.io/phusion/baseimage:noble-1.0.0
2
2
 
3
3
  # Use baseimage-docker's init system.
4
4
  CMD ["/sbin/my_init"]
@@ -16,6 +16,8 @@ EOF
16
16
  function show() {
17
17
  if [ "$*" == "version" ]; then
18
18
  echo "Version 1.2.3"
19
+ # Make the output change over time
20
+ date
19
21
  elif [ "$*" == "runningconfiguration all" ]; then
20
22
  cat << EOF
21
23
  ! begin of the configuration
@@ -3,6 +3,8 @@ config.local
3
3
  router.db.local
4
4
 
5
5
  # Ignore logs, retrieved configs...
6
+ pid
6
7
  configs/
7
8
  crash
8
9
  logs/
10
+ oxidized.git/
@@ -10,7 +10,7 @@ use_max_threads: true
10
10
  timeout: 20
11
11
  retries: 3
12
12
  prompt: !ruby/regexp /^([\w.@-]+[#>]\s?)$/
13
- rest: 127.0.0.1:8888
13
+ rest: 0.0.0.0:8888
14
14
  next_adds_job: false
15
15
  vars: {}
16
16
  groups: {}
@@ -0,0 +1,46 @@
1
+ ---
2
+ username: oxidized
3
+ password: oxidized
4
+ resolve_dns: true
5
+ interval: 3600
6
+ use_syslog: false
7
+ debug: false
8
+ threads: 30
9
+ use_max_threads: true
10
+ timeout: 20
11
+ retries: 3
12
+ prompt: !ruby/regexp /^([\w.@-]+[#>]\s?)$/
13
+ rest: 0.0.0.0:8888
14
+ next_adds_job: false
15
+ vars: {}
16
+ groups: {}
17
+ group_map: {}
18
+ models: {}
19
+ pid: "~/.config/oxidized/pid"
20
+ crash:
21
+ directory: "~/.config/oxidized/crashes"
22
+ hostnames: false
23
+ stats:
24
+ history_size: 10
25
+ input:
26
+ default: ssh
27
+ debug: false
28
+ ssh:
29
+ secure: false
30
+ ftp:
31
+ passive: true
32
+ utf8_encoded: true
33
+ output:
34
+ default: file
35
+ file:
36
+ directory: "~/.config/oxidized/configs/"
37
+ source:
38
+ default: csv
39
+ csv:
40
+ file: "~/.config/oxidized/router.db"
41
+ delimiter: !ruby/regexp /:/
42
+ map:
43
+ name: 0
44
+ model: 1
45
+ ip: 2
46
+ gpg: false
@@ -0,0 +1,56 @@
1
+ ---
2
+ username: oxidized
3
+ password: oxidized
4
+ resolve_dns: true
5
+ interval: 3600
6
+ use_syslog: false
7
+ debug: false
8
+ threads: 30
9
+ use_max_threads: true
10
+ timeout: 20
11
+ retries: 3
12
+ prompt: !ruby/regexp /^([\w.@-]+[#>]\s?)$/
13
+ rest: 0.0.0.0:8888
14
+ next_adds_job: false
15
+ vars: {}
16
+ groups: {}
17
+ group_map: {}
18
+ models: {}
19
+ pid: "~/.config/oxidized/pid"
20
+ crash:
21
+ directory: "~/.config/oxidized/crashes"
22
+ hostnames: false
23
+ stats:
24
+ history_size: 10
25
+ input:
26
+ default: ssh
27
+ debug: false
28
+ ssh:
29
+ secure: false
30
+ ftp:
31
+ passive: true
32
+ utf8_encoded: true
33
+ output:
34
+ default: git
35
+ git:
36
+ user: Oxidized
37
+ email: o@example.com
38
+ repo: "~/.config/oxidized/oxidized.git"
39
+ source:
40
+ default: csv
41
+ csv:
42
+ file: "~/.config/oxidized/router.db"
43
+ delimiter: !ruby/regexp /:/
44
+ map:
45
+ name: 0
46
+ model: 1
47
+ ip: 2
48
+ gpg: false
49
+ hooks:
50
+ push_to_remote:
51
+ type: githubrepo
52
+ events:
53
+ - post_store
54
+ remote_repo: git@gitserver:repo.git
55
+ username: git
56
+ password: git
@@ -0,0 +1 @@
1
+ known_hosts
@@ -17,6 +17,8 @@ module Oxidized
17
17
  def self.load(cmd_opts = {})
18
18
  usrdir = File.expand_path(cmd_opts[:home_dir] || Oxidized::Config::ROOT)
19
19
  cfgfile = cmd_opts[:config_file] || 'config'
20
+ # configuration file with full path as a class instance variable
21
+ @configfile = File.join(usrdir, cfgfile)
20
22
  asetus = Asetus.new(name: 'oxidized', load: false, key_to_s: true, usrdir: usrdir, cfgfile: cfgfile)
21
23
  Oxidized.asetus = asetus
22
24
 
@@ -65,13 +67,17 @@ module Oxidized
65
67
  raise InvalidConfig, "Error loading config: #{e.message}"
66
68
  end
67
69
 
68
- raise NoConfig, 'edit ~/.config/oxidized/config' if asetus.create
70
+ raise NoConfig, "edit #{@configfile}" if asetus.create
69
71
 
70
72
  # override if comand line flag given
71
73
  asetus.cfg.debug = cmd_opts[:debug] if cmd_opts[:debug]
72
74
 
73
75
  asetus
74
76
  end
77
+
78
+ class << self
79
+ attr_reader :configfile
80
+ end
75
81
  end
76
82
 
77
83
  class << self
@@ -1,15 +1,21 @@
1
+ require 'rugged'
2
+
1
3
  class GithubRepo < Oxidized::Hook
2
4
  def validate_cfg!
3
5
  raise KeyError, 'hook.remote_repo is required' unless cfg.has_key?('remote_repo')
4
6
  end
5
7
 
6
8
  def run_hook(ctx)
9
+ unless ctx.node.repo
10
+ log "Oxidized output is not git, can't push to remote", :error
11
+ return
12
+ end
7
13
  repo = Rugged::Repository.new(ctx.node.repo)
8
14
  creds = credentials(ctx.node)
9
15
  url = remote_repo(ctx.node)
10
16
 
11
17
  if url.nil? || url.empty?
12
- log "No repository defined for #{ctx.node.group}/#{ctx.node.name}", :debug
18
+ log "No repository defined for #{ctx.node.group}/#{ctx.node.name}", :error
13
19
  return
14
20
  end
15
21
 
@@ -23,21 +29,39 @@ class GithubRepo < Oxidized::Hook
23
29
  end
24
30
  remote = repo.remotes['origin']
25
31
 
26
- fetch_and_merge_remote(repo, creds)
27
-
28
- remote.push([repo.head.name], credentials: creds)
32
+ begin
33
+ fetch_and_merge_remote(repo, creds)
34
+ remote.push([repo.head.name], credentials: creds)
35
+ rescue Rugged::NetworkError => e
36
+ if e.message == 'unsupported URL protocol'
37
+ log "Rugged does not support the git URL '#{url}'.", :warn
38
+ unless Rugged.features.include?(:ssh)
39
+ log 'You may need to install Rugged with ssh support ' \
40
+ '(gem install rugged -- --with-ssh)', :warn
41
+ end
42
+ end
43
+ # re-raise exception for the calling method
44
+ raise
45
+ end
29
46
  end
30
47
 
31
48
  def fetch_and_merge_remote(repo, creds)
32
49
  result = repo.fetch('origin', [repo.head.name], credentials: creds)
33
50
  log result.inspect, :debug
34
51
 
35
- unless result[:total_deltas].positive?
36
- log "nothing received after fetch", :debug
52
+ their_branch = remote_branch(repo)
53
+
54
+ unless their_branch
55
+ log 'remote branch does not exist yet, nothing to merge', :debug
37
56
  return
38
57
  end
39
58
 
40
- their_branch = repo.branches["origin/master"]
59
+ result = repo.merge_analysis(their_branch.target_id)
60
+
61
+ if result.include? :up_to_date
62
+ log 'nothing to merge', :debug
63
+ return
64
+ end
41
65
 
42
66
  log "merging fetched branch #{their_branch.name}", :debug
43
67
 
@@ -97,4 +121,10 @@ class GithubRepo < Oxidized::Hook
97
121
  cfg.remote_repo[node.group].url
98
122
  end
99
123
  end
124
+
125
+ # Returns a Rugged::Branch to the remote branch or nil if it doen't exist
126
+ def remote_branch(repo)
127
+ head_branch = repo.branches[repo.head.name]
128
+ repo.branches['origin/' + head_branch.name]
129
+ end
100
130
  end
@@ -1,4 +1,6 @@
1
- require 'slack'
1
+ require 'slack_ruby_client'
2
+ require 'uri'
3
+ require 'net/http'
2
4
 
3
5
  # defaults to posting a diff, if messageformat is supplied them a message will be posted too
4
6
  # diff defaults to true
@@ -9,6 +11,30 @@ class SlackDiff < Oxidized::Hook
9
11
  raise KeyError, 'hook.channel is required' unless cfg.has_key?('channel')
10
12
  end
11
13
 
14
+ def slack_upload(client, title, content, channel)
15
+ log "Posting diff as snippet to #{channel}"
16
+ upload_dest = client.files_getUploadURLExternal(filename: "change",
17
+ length: content.length,
18
+ snippet_type: "diff")
19
+ file_uri = URI.parse(upload_dest[:upload_url])
20
+
21
+ http = Net::HTTP.new(file_uri.host, file_uri.port)
22
+ http.use_ssl = true
23
+
24
+ request = Net::HTTP::Post.new(file_uri.request_uri, { Host: file_uri.host })
25
+ request.body = content
26
+ response = http.request(request)
27
+
28
+ raise 'Slack file upload failed' unless response.is_a? Net::HTTPSuccess
29
+
30
+ files = [{
31
+ id: upload_dest[:file_id],
32
+ title: title
33
+ }]
34
+ client.files_completeUploadExternal(channel_id: channel,
35
+ files: files.to_json)
36
+ end
37
+
12
38
  def run_hook(ctx)
13
39
  return unless ctx.node
14
40
  return unless ctx.event.to_s == "post_store"
@@ -26,12 +52,8 @@ class SlackDiff < Oxidized::Hook
26
52
  diff = gitoutput.get_diff ctx.node, ctx.node.group, ctx.commitref, nil
27
53
  unless diff == "no diffs"
28
54
  title = "#{ctx.node.name} #{ctx.node.group} #{ctx.node.model.class.name.to_s.downcase}"
29
- log "Posting diff as snippet to #{cfg.channel}"
30
- client.files_upload(channels: cfg.channel, as_user: true,
31
- content: diff[:patch].lines.to_a[4..-1].join,
32
- filetype: "diff",
33
- title: title,
34
- filename: "change")
55
+ content = diff[:patch].lines.to_a[4..-1].join
56
+ slack_upload(client, title, content, cfg.channel)
35
57
  end
36
58
  end
37
59
  # message custom formatted - optional
@@ -2,6 +2,7 @@ module Oxidized
2
2
  require "oxidized/input/cli"
3
3
  require "net/http"
4
4
  require "json"
5
+ require "net/http/digest_auth"
5
6
 
6
7
  class HTTP < Input
7
8
  include Input::CLI
@@ -63,7 +63,7 @@ module Oxidized
63
63
  def disconnect
64
64
  disconnect_cli
65
65
  @telnet.close
66
- rescue Errno::ECONNRESET
66
+ rescue Errno::ECONNRESET, IOError
67
67
  # This exception is intented and therefore not handled here
68
68
  ensure
69
69
  @log.close if Oxidized.config.input.debug?
@@ -5,15 +5,16 @@ module Oxidized
5
5
  require 'oxidized/source/source'
6
6
  class Manager
7
7
  class << self
8
- def load(dir, file)
8
+ def load(dir, file, namespace)
9
9
  require File.join dir, file + '.rb'
10
- klass = nil
11
- [Oxidized, Object].each do |mod|
12
- klass = mod.constants.find { |const| const.to_s.casecmp(file).zero? }
13
- klass ||= mod.constants.find { |const| const.to_s.downcase == 'oxidized' + file.downcase }
14
- klass = mod.const_get klass if klass
15
- break if klass
16
- end
10
+
11
+ # Search the object to load in namespace
12
+ klass = namespace.constants.find { |const| const.to_s.casecmp(file).zero? }
13
+
14
+ return false unless klass
15
+
16
+ klass = namespace.const_get klass
17
+
17
18
  i = klass.new
18
19
  i.setup if i.respond_to? :setup
19
20
  { file => klass }
@@ -33,32 +34,32 @@ module Oxidized
33
34
  end
34
35
 
35
36
  def add_input(name)
36
- loader @input, Config::INPUT_DIR, "input", name
37
+ loader @input, Config::INPUT_DIR, "input", name, Oxidized
37
38
  end
38
39
 
39
40
  def add_output(name)
40
- loader @output, Config::OUTPUT_DIR, "output", name
41
+ loader @output, Config::OUTPUT_DIR, "output", name, Oxidized::Output
41
42
  end
42
43
 
43
44
  def add_source(name)
44
- loader @source, Config::SOURCE_DIR, "source", name
45
+ loader @source, Config::SOURCE_DIR, "source", name, Oxidized::Source
45
46
  end
46
47
 
47
48
  def add_model(name)
48
- loader @model, Config::MODEL_DIR, "model", name
49
+ loader @model, Config::MODEL_DIR, "model", name, Object
49
50
  end
50
51
 
51
52
  def add_hook(name)
52
- loader @hook, Config::HOOK_DIR, "hook", name
53
+ loader @hook, Config::HOOK_DIR, "hook", name, Object
53
54
  end
54
55
 
55
56
  private
56
57
 
57
58
  # if local version of file exists, load it, else load global - return falsy value if nothing loaded
58
- def loader(hash, global_dir, local_dir, name)
59
+ def loader(hash, global_dir, local_dir, name, namespace)
59
60
  dir = File.join(Config::ROOT, local_dir)
60
- map = Manager.load(dir, name) if File.exist? File.join(dir, name + ".rb")
61
- map ||= Manager.load(global_dir, name)
61
+ map = Manager.load(dir, name, namespace) if File.exist? File.join(dir, name + ".rb")
62
+ map ||= Manager.load(global_dir, name, namespace)
62
63
  hash.merge!(map) if map
63
64
  end
64
65
  end
@@ -56,9 +56,23 @@ class Aoscx < Oxidized::Model
56
56
  cmd 'show environment' do |cfg|
57
57
  cfg.gsub! /^(LC.*\s+)\d+\s+$/, '\\1<hidden>'
58
58
  cfg.gsub! /^(\d\/\d\/\d.*\s+)\d+\s+$/, '\\1<hidden>'
59
- cfg.gsub! /^(\d+\/\S+\s+\S+\s+)\d+\.\d+\s+C(.*)/, '\\1<hidden>\\2'
59
+ cfg.gsub! /^(\d+\/?\S+\s+\S+\s+)\d+\.\d+\s+C\s+(.*)/, '\\1<hidden> \\2'
60
60
  cfg.gsub! /^(LC.*\s+)\d+\.\d+\s+(C.*)$/, '\\1 <hidden> \\2'
61
- cfg.gsub! /^(\S+\s+\S+\s+\s+\S+\s+)(slow|normal|medium|fast|max)(\s+\S+\s+\S+\s+)\d+/, '\\1<speed>\\3<rpm>'
61
+ 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>'
62
+ # 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"
63
+ 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>'
64
+ # 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"
65
+ cfg.gsub! /^(\d+\/?\d*\s+.+\s+)(\s{1,4}\d{1,3})\sW\s*$/, '\\1<power>'
66
+ # 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.
67
+ 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>'
68
+ # 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.
69
+ 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>'
70
+ # match show environment power-consumption on 8400 or chassis switches, up to a 5-digit number, example matches:
71
+ # e.g. "Chassis Total Power Allocated (total of all max wattages) 4130 W"
72
+ # e.g. "Chassis Total Power Unallocated 15860 W"
73
+ 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>'
74
+ # match Total Power Consumption:
75
+ cfg.gsub! /^([t|T]otal\s[p|P]ower\s[c|C]onsumption\s+)(\d+\.\d\d)$/, '\\1<power>'
62
76
  comment cfg
63
77
  end
64
78
 
@@ -12,7 +12,13 @@ class AOSW < Oxidized::Model
12
12
  # All IAPs connected to a Instant Controller will have the same config output. Only the controller needs to be monitored.
13
13
 
14
14
  comment '# '
15
- prompt /^([\w\(:.@-]+(\)?\s?)[#>]\s?)$/
15
+ # see /spec/model/aosw_spec.rb for prompt examples
16
+ prompt /^\(?[\w\:.@-]+\)? ?[*^]?(\[[\w\/]+\] ?)?[#>] ?$/
17
+
18
+ # Ignore cariage returns - also for the prompt
19
+ expect "\r" do |data, re|
20
+ data.gsub re, ''
21
+ end
16
22
 
17
23
  cmd :all do |cfg|
18
24
  cfg.cut_both
@@ -0,0 +1,90 @@
1
+ class ArubaInstant < Oxidized::Model
2
+ using Refinements
3
+
4
+ # Aruba IAP, Instant Controller
5
+
6
+ comment '# '
7
+ prompt(/^[\w\:.@-]+[#>] $/)
8
+
9
+ cmd :all do |cfg|
10
+ # Remove command echo and prompt
11
+ cfg.cut_both
12
+ end
13
+
14
+ cmd :secret do |cfg|
15
+ cfg.gsub!(/ipsec (\S+)$/, 'ipsec <secret removed>')
16
+ cfg.gsub!(/community (\S+)$/, 'community <secret removed>')
17
+ cfg.gsub!(/^(snmp-server host [\d.]+ version 2c) \S+ (.*)$/, '\1 <secret removed> \2')
18
+ # MAS format: mgmt-user <username> <accesslevel> <password hash>
19
+ # IAP format (root user): mgmt-user <username> <password hash>
20
+ # IAP format: mgmt-user <username> <password hash> <access level>
21
+ cfg.gsub!(/mgmt-user (\S+) (root|guest-provisioning|network-operations|read-only|location-api-mgmt) (\S+)$/, 'mgmt-user \1 \2 <secret removed>') # MAS & Wireless Controler
22
+ cfg.gsub!(/mgmt-user (\S+) (\S+)( (read-only|guest-mgmt))?$/, 'mgmt-user \1 <secret removed> \3') # IAP
23
+ cfg.gsub!(/key (\S+)$/, 'key <secret removed>')
24
+ cfg.gsub!(/wpa-passphrase (\S+)$/, 'wpa-passphrase <secret removed>')
25
+ cfg.gsub!(/bkup-passwords (\S+)$/, 'bkup-passwords <secret removed>')
26
+ cfg.gsub!(/user (\S+) (\S+) (\S+)$/, 'user \1 <secret removed> \3')
27
+ cfg.gsub!(/virtual-controller-key (\S+)$/, 'virtual-controller-key <secret removed>')
28
+ cfg.gsub!(/^(hash-mgmt-user .* password \S+) \S+( usertype .*)?$/, '\1 <secret removed>\2')
29
+ cfg
30
+ end
31
+
32
+ # get software version
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
43
+ end
44
+
45
+ # Get serial number
46
+ 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"
58
+ end
59
+
60
+ # Get controlled WLAN-AP
61
+ cmd 'show aps' do |cfg|
62
+ out = ''
63
+ 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
69
+ end
70
+ comment out + "\n"
71
+ end
72
+
73
+ cmd 'show running-config no-encrypt'
74
+
75
+ cfg :telnet do
76
+ username(/^User:\s*/)
77
+ password(/^Password:\s*/)
78
+ end
79
+
80
+ cfg :telnet, :ssh do
81
+ if vars :enable
82
+ post_login do
83
+ cmd "enable", /^[pP]assword:/
84
+ cmd vars(:enable)
85
+ end
86
+ end
87
+ pre_logout 'exit' if vars :enable
88
+ pre_logout 'exit'
89
+ end
90
+ end
@@ -12,14 +12,14 @@ class AudioCodes < Oxidized::Model
12
12
  data.sub re, ''
13
13
  end
14
14
 
15
- cmd 'show running-config' do |cfg|
15
+ cmd "show running-config\r\n" do |cfg|
16
16
  cfg
17
17
  end
18
18
 
19
19
  cfg :ssh do
20
20
  username /^login as:\s$/
21
21
  password /^.+password:\s$/
22
- pre_logout 'exit'
22
+ pre_logout "exit\r\n"
23
23
  end
24
24
 
25
25
  cfg :telnet do
@@ -4,18 +4,20 @@ class CNOS < Oxidized::Model
4
4
 
5
5
  comment '! '
6
6
 
7
- cmd :all do |cfg|
8
- cfg.each_line.to_a[0..-2].join
7
+ cmd :secret do |cfg|
8
+ cfg.gsub! /^(snmp-server community).*/, '\\1 <configuration removed>'
9
+ cfg.gsub! /^(username .+ (password|secret) \d) .+/, '\\1 <secret hidden>'
10
+ cfg.gsub! /^(enable (password|secret)( level \d+)?( \d)?) .+/, '\\1 <secret hidden>'
11
+ cfg
9
12
  end
10
13
 
11
- cmd 'show running-config' do |cfg|
12
- cfg.gsub!(/(snmp-server community )(\S+)/, '\1<hidden>')
13
- cfg.gsub!(/key type private.+key string end/m, '<private key hidden>')
14
- cfg
14
+ cmd :all do |cfg|
15
+ cfg = cfg.delete("\r")
16
+ cfg.cut_both
15
17
  end
16
18
 
17
19
  cmd 'show version' do |cfg|
18
- cfg.gsub! /^(.* uptime is ).*\n/, '\1'
20
+ cfg = cfg.each_line.reject { |line| line.match /\ uptime\ is\ / }.join
19
21
  comment cfg
20
22
  end
21
23
 
@@ -23,9 +25,10 @@ class CNOS < Oxidized::Model
23
25
  comment cfg
24
26
  end
25
27
 
26
- cfg :telnet do
27
- username /^Username:/
28
- password /^Password:/
28
+ cmd 'show running-config' do |cfg|
29
+ # remove empty lines
30
+ cfg = cfg.each_line.reject { |line| line.match /^[\r\n\s\u0000#]+$/ }.join
31
+ cfg
29
32
  end
30
33
 
31
34
  cfg :telnet, :ssh do
@@ -21,9 +21,12 @@ class Cumulus < Oxidized::Model
21
21
  # show the persistent configuration
22
22
  pre do
23
23
  use_nclu = vars(:cumulus_use_nclu) || false
24
+ use_nvue = vars(:cumulus_use_nvue) || false
24
25
 
25
26
  if use_nclu
26
27
  cfg = cmd 'net show configuration commands'
28
+ elsif use_nvue
29
+ cfg = cmd 'nv config show --color off'
27
30
  else
28
31
  # Set FRR or Quagga in config
29
32
  routing_daemon = vars(:cumulus_routing_daemon) ? vars(:cumulus_routing_daemon).downcase : 'quagga'
@@ -37,6 +37,7 @@ class Dlink < Oxidized::Model
37
37
 
38
38
  cfg :telnet, :ssh do
39
39
  post_login 'disable clipaging'
40
+ post_login 'enable admin' if vars(:enable) == true
40
41
  pre_logout 'logout'
41
42
  end
42
43
  end
@@ -35,6 +35,9 @@ class DlinkNextGen < Oxidized::Model
35
35
  cmd 'show running-config' do |cfg|
36
36
  cfg.gsub! /^(snmp-server community ["\w]+) \S+/, '\\1 <removed>'
37
37
  cfg.gsub! /^(username [\w.@-]+ privilege \d{1,2} password \d{1,2}) \S+/, '\\1 <removed>'
38
+ cfg.gsub! /^(!System Up Time).*/, '\\1 <removed>'
39
+ cfg.gsub! /^(!Current SNTP Synchronized Time:).*/, '\\1 <removed>'
40
+ cfg.gsub! /^(\s+ppp (chap|pap) password \d) .+/, '\\1 <secret hidden>'
38
41
  cfg
39
42
  end
40
43