oxidized 0.30.1 → 0.31.0

Sign up to get free protection for your applications and to get access to all the features.
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