oxidized 0.32.1 → 0.34.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 (127) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +45 -0
  3. data/.github/ISSUE_TEMPLATE/feature_request.md +22 -0
  4. data/.github/ISSUE_TEMPLATE/support-request.md +39 -0
  5. data/.github/workflows/publishdocker.yml +35 -16
  6. data/.github/workflows/ruby.yml +4 -2
  7. data/.gitignore +2 -0
  8. data/.rubocop.yml +29 -8
  9. data/.rubocop_todo.yml +1 -60
  10. data/CHANGELOG.md +103 -2
  11. data/CONTRIBUTING.md +20 -10
  12. data/Dockerfile +37 -64
  13. data/README.md +47 -141
  14. data/Rakefile +9 -11
  15. data/docs/Configuration.md +236 -27
  16. data/docs/DeviceSimulation.md +19 -7
  17. data/docs/Docker.md +245 -0
  18. data/docs/Issues.md +27 -1
  19. data/docs/Model-Notes/EatonNetwork.md +18 -0
  20. data/docs/Model-Notes/HPEAruba.md +3 -2
  21. data/docs/ModelUnitTests.md +35 -25
  22. data/docs/Outputs.md +83 -2
  23. data/docs/Release.md +34 -24
  24. data/docs/Supported-OS-Types.md +7 -0
  25. data/docs/Troubleshooting.md +4 -13
  26. data/extra/device2yaml.rb +24 -9
  27. data/extra/rest_client.rb +3 -2
  28. data/extra/syslog.rb +8 -3
  29. data/lib/oxidized/cli.rb +7 -3
  30. data/lib/oxidized/config/vars.rb +22 -14
  31. data/lib/oxidized/config.rb +3 -2
  32. data/lib/oxidized/core.rb +30 -8
  33. data/lib/oxidized/hook/ciscosparkdiff.rb +11 -9
  34. data/lib/oxidized/hook/exec.rb +5 -4
  35. data/lib/oxidized/hook/githubrepo.rb +23 -17
  36. data/lib/oxidized/hook/noophook.rb +2 -2
  37. data/lib/oxidized/hook/slackdiff.rb +9 -8
  38. data/lib/oxidized/hook/xmppdiff.rb +9 -9
  39. data/lib/oxidized/hook.rb +10 -8
  40. data/lib/oxidized/input/cli.rb +8 -3
  41. data/lib/oxidized/input/exec.rb +1 -1
  42. data/lib/oxidized/input/ftp.rb +2 -2
  43. data/lib/oxidized/input/http.rb +6 -6
  44. data/lib/oxidized/input/input.rb +1 -0
  45. data/lib/oxidized/input/scp.rb +2 -2
  46. data/lib/oxidized/input/ssh.rb +21 -14
  47. data/lib/oxidized/input/telnet.rb +3 -3
  48. data/lib/oxidized/input/tftp.rb +1 -1
  49. data/lib/oxidized/job.rb +7 -4
  50. data/lib/oxidized/logger.rb +51 -0
  51. data/lib/oxidized/model/acos.rb +1 -0
  52. data/lib/oxidized/model/aos7.rb +9 -0
  53. data/lib/oxidized/model/aoscx.rb +2 -0
  54. data/lib/oxidized/model/aosw.rb +22 -17
  55. data/lib/oxidized/model/aricentiss.rb +2 -2
  56. data/lib/oxidized/model/asa.rb +3 -3
  57. data/lib/oxidized/model/awplus.rb +13 -10
  58. data/lib/oxidized/model/eatonnetwork.rb +65 -0
  59. data/lib/oxidized/model/edgecos.rb +2 -1
  60. data/lib/oxidized/model/edgeos.rb +7 -6
  61. data/lib/oxidized/model/edgeswitch.rb +3 -1
  62. data/lib/oxidized/model/efos.rb +41 -0
  63. data/lib/oxidized/model/eltex.rb +1 -1
  64. data/lib/oxidized/model/fabricos.rb +1 -1
  65. data/lib/oxidized/model/fastiron.rb +3 -1
  66. data/lib/oxidized/model/firelinuxos.rb +12 -3
  67. data/lib/oxidized/model/fortios.rb +5 -4
  68. data/lib/oxidized/model/gaiaos.rb +4 -4
  69. data/lib/oxidized/model/ingate.rb +47 -0
  70. data/lib/oxidized/model/ios.rb +16 -5
  71. data/lib/oxidized/model/ironware.rb +1 -1
  72. data/lib/oxidized/model/junos.rb +4 -0
  73. data/lib/oxidized/model/linksyssrw.rb +3 -3
  74. data/lib/oxidized/model/mlnxos.rb +14 -7
  75. data/lib/oxidized/model/model.rb +4 -3
  76. data/lib/oxidized/model/netgear.rb +8 -0
  77. data/lib/oxidized/model/nsxdfw.rb +2 -1
  78. data/lib/oxidized/model/nsxfirewall.rb +2 -1
  79. data/lib/oxidized/model/nxos.rb +2 -2
  80. data/lib/oxidized/model/openwrt.rb +6 -6
  81. data/lib/oxidized/model/powerconnect.rb +31 -10
  82. data/lib/oxidized/model/procurve.rb +3 -1
  83. data/lib/oxidized/model/qtech.rb +3 -1
  84. data/lib/oxidized/model/quantaos.rb +8 -6
  85. data/lib/oxidized/model/routeros.rb +3 -2
  86. data/lib/oxidized/model/saos10.rb +38 -0
  87. data/lib/oxidized/model/sixwind.rb +28 -0
  88. data/lib/oxidized/model/sonicos.rb +1 -1
  89. data/lib/oxidized/model/srosmd.rb +1 -1
  90. data/lib/oxidized/model/supermicro.rb +1 -1
  91. data/lib/oxidized/model/timos.rb +1 -1
  92. data/lib/oxidized/model/tmos.rb +1 -0
  93. data/lib/oxidized/model/tnsr.rb +53 -0
  94. data/lib/oxidized/model/trango.rb +3 -1
  95. data/lib/oxidized/model/unifiap.rb +144 -0
  96. data/lib/oxidized/model/vrp.rb +3 -1
  97. data/lib/oxidized/model/xos.rb +3 -1
  98. data/lib/oxidized/model/zhoneolt.rb +3 -1
  99. data/lib/oxidized/model/zynos.rb +3 -3
  100. data/lib/oxidized/node.rb +44 -27
  101. data/lib/oxidized/nodes.rb +8 -4
  102. data/lib/oxidized/output/file.rb +28 -0
  103. data/lib/oxidized/output/git.rb +148 -41
  104. data/lib/oxidized/output/gitcrypt.rb +18 -13
  105. data/lib/oxidized/output/http.rb +5 -4
  106. data/lib/oxidized/output/output.rb +14 -0
  107. data/lib/oxidized/source/http.rb +4 -2
  108. data/lib/oxidized/version.rb +6 -4
  109. data/lib/oxidized/worker.rb +13 -13
  110. data/lib/oxidized.rb +3 -24
  111. data/lib/refinements.rb +2 -0
  112. data/oxidized.gemspec +10 -8
  113. metadata +74 -41
  114. data/examples/podman-compose/Makefile +0 -103
  115. data/examples/podman-compose/README.md +0 -94
  116. data/examples/podman-compose/docker-compose.yml +0 -30
  117. data/examples/podman-compose/gitserver/.gitignore +0 -1
  118. data/examples/podman-compose/gitserver/Dockerfile +0 -14
  119. data/examples/podman-compose/model-simulation/Dockerfile-model +0 -13
  120. data/examples/podman-compose/model-simulation/asternos.sh +0 -36
  121. data/examples/podman-compose/oxidized-config/.gitignore +0 -10
  122. data/examples/podman-compose/oxidized-config/config +0 -46
  123. data/examples/podman-compose/oxidized-config/config_csv-file +0 -46
  124. data/examples/podman-compose/oxidized-config/config_csv-gitserver +0 -56
  125. data/examples/podman-compose/oxidized-config/router.db +0 -1
  126. data/examples/podman-compose/oxidized-ssh/.gitignore +0 -1
  127. data/examples/podman-compose/oxidized-ssh/README.md +0 -14
@@ -2,6 +2,7 @@
2
2
 
3
3
  |Vendor |OS model |oxidized model |model maintainers|comment / model notes|
4
4
  |--------------------|------------------------------|-------------------------------------------------|-----------------|---------------------|
5
+ |6WIND |VSR |[sixwind](/lib/oxidized/model/sixwind.rb) |@hcaldicott |
5
6
  |A10 Networks |ACOS |[acos](/lib/oxidized/model/acos.rb) | |
6
7
  |Accedian Performance Elements (NIDs)|AEN |[aen](/lib/oxidized/model/aen.rb)
7
8
  |Acme Packet |ACMEPACKET |[acmepacket](/lib/oxidized/model/acmepacket.rb)
@@ -30,6 +31,7 @@
30
31
  | |BOSS (Baystack Operating System Software)|[boss](/lib/oxidized/model/boss.rb)
31
32
  |BDCOM |S2200PB, S2200-B, S2500-B, S2500-C, S2500PB, S2500-P, S2900 series|[bdcom](/lib/oxidized/model/bdcom.rb)
32
33
  |Brocade |FabricOS |[fabricos](/lib/oxidized/model/fabricos.rb)
34
+ | |Enhanced Fabric OS |[efos](/lib/oxidized/model/efos.rb)
33
35
  | |FastIron |[fastiron](/lib/oxidized/model/fastiron.rb)
34
36
  | |IronWare |[ironware](/lib/oxidized/model/ironware.rb)
35
37
  | |NOS (Network Operating System)|[nos](/lib/oxidized/model/nos.rb)
@@ -43,6 +45,7 @@
43
45
  |Centec Networks |CNOS |[cnos](/lib/oxidized/model/cnos.rb)
44
46
  |Check Point |GaiaOS |[gaiaos](/lib/oxidized/model/gaiaos.rb)
45
47
  |Ciena |SAOS |[saos](/lib/oxidized/model/saos.rb)
48
+ | |SAOS10 |[saos10](/lib/oxidized/model/saos10.rb)
46
49
  |Cisco |ACSW |[acsw](/lib/oxidized/model/acsw.rb)
47
50
  | |AireOS |[aireos](/lib/oxidized/model/aireos.rb) | |[AireOS](Model-Notes/AireOS.md)
48
51
  | |ASA |[asa](/lib/oxidized/model/asa.rb) |@robertcheramy
@@ -75,6 +78,7 @@
75
78
  | |Dell EMC Networking OS10 |[os10](/lib/oxidized/model/os10.rb) | |[Dell EMC Networking OS10](Model-Notes/OS10.md)
76
79
  |D-Link |D-Link |[dlink](/lib/oxidized/model/dlink.rb)
77
80
  | |D-Link cisco like CLI |[dlinknextgen](/lib/oxidized/model/dlinknextgen.rb)
81
+ |Eaton |Gigabit Network Card |[eatonnetwork](/lib/oxidized/model/eatonnetwork.rb) |@thanegill
78
82
  |ECI Telecom |ECIapollo |[eciapollo](/lib/oxidized/model/eciapollo.rb)
79
83
  |EdgeCore |ECS3510, ES3526XA-V2, ES3528M |[edgecos](/lib/oxidized/model/edgecos.rb)
80
84
  |Eltex |Eltex |[eltex](/lib/oxidized/model/eltex.rb)
@@ -113,6 +117,7 @@
113
117
  |Huawei |VRP |[vrp](/lib/oxidized/model/vrp.rb) | |[VRP-Huawei](Model-Notes/VRP-Huawei.md)
114
118
  | |SmartAX series |[smartax](/lib/oxidized/model/smartax.rb) | |[SmartAX-Huawei](Model-Notes/SmartAX-Huawei.md)
115
119
  |Icotera |6400 series |[icotera](/lib/oxidized/model/icotera.rb)
120
+ |Ingate |SIParator/Firewalls |[ingate](/lib/oxidized/model/ingate.rb) |@thanegill
116
121
  |IP Infusion |OcNOS |[ocnos](/lib/oxidized/model/ocnos.rb)
117
122
  |Juniper |JunOS |[junos](/lib/oxidized/model/junos.rb) | |[MX/QFX/EX/SRX/J Series](Model-Notes/JunOS.md)
118
123
  | |ScreenOS (Netscreen) |[screenos](/lib/oxidized/model/screenos.rb)
@@ -129,6 +134,7 @@
129
134
  |MRV |MasterOS |[masteros](/lib/oxidized/model/masteros.rb)
130
135
  | |FiberDriver |[fiberdriver](/lib/oxidized/model/fiberdriver.rb)
131
136
  |NEC |NEC IX |[necix](/lib/oxidized/model/necix.rb)
137
+ |Netgate |TNSR |[tnsr](/lib/oxidized/model/tnsr.rb) |@Vantomas
132
138
  |Netgear |Netgear switches |[netgear](/lib/oxidized/model/netgear.rb) | |[Netgear](Model-Notes/Netgear.md)
133
139
  |Netonix |WISP Switch (As Netonix) |[netonix](/lib/oxidized/model/netonix.rb)
134
140
  |Nokia (formerly TiMetra, Alcatel, Alcatel-Lucent)|SR OS (TiMOS)|[sros](/lib/oxidized/model/sros.rb) | |[Nokia ISAM](Model-Notes/Nokia.md)
@@ -169,6 +175,7 @@
169
175
  | |Edgeos |[edgeos](/lib/oxidized/model/edgeos.rb)
170
176
  | |EdgeSwitch |[edgeswitch](/lib/oxidized/model/edgeswitch.rb)
171
177
  | |AirFiber |[airfiber](/lib/oxidized/model/airfiber.rb)
178
+ | |UnifiAP |[unifiap](/lib/oxidized/model/unifiap.rb) |@clifcox |Also suports AirOS, and some Unifi switches
172
179
  |Uplink |EP4440-DP |[EP4440](/lib/oxidized/model/uplinkolt.rb) | |Might support all EP4440 series
173
180
  |VMWare |NSX Edge (configuration) |[nsxconfig](/lib/oxidized/model/nsxconfig.rb)
174
181
  | |NSX Edge (firewall rules) |[nsxfirewall](/lib/oxidized/model/nsxfirewall.rb)
@@ -27,7 +27,10 @@ Welcome to the advanced nuclear launchinator 5A-X20. Proceed with caution.
27
27
  SEKRET-5A-X20#
28
28
  ```
29
29
 
30
- Review the relevant device model file and identify the defined prompt. You can find the device models in the `lib/oxidized/model` sub-folder of the repository. For example, the Cisco IOS model, `ios.rb` may use the following prompt:
30
+ Review the relevant device model file and identify the defined prompt. You can
31
+ find the device models in the `lib/oxidized/model` subdirectory of the
32
+ repository. For example, the Cisco IOS model, `ios.rb` may use the following
33
+ prompt:
31
34
 
32
35
  ```text
33
36
  prompt /^([\w.@()-]+[#>]\s?)$/
@@ -85,18 +88,6 @@ If you are running oxidized in a container, you need to map /home/oxidized/.ssh
85
88
  container to a local repository and save the known_hosts in the local repository. You can
86
89
  find an example how to do this under [examples/podman-compose](/examples/podman-compose/)
87
90
 
88
- ## Git performance issues with large device counts
89
- When you use git to store your configurations, the size of your repository will
90
- grow over time. This growth can lead to performance issues. To resolve these issues, you should perform a Git garbage collection on your repository.
91
-
92
- Follow these steps to do so:
93
-
94
- 1. Stop oxidized (no one should access the git repository while running garbage collection)
95
- 2. Make a backup of your oxidized data, especially the Git repository
96
- 3. Change directory your oxidized git repository (as configured in oxidized configuration file)
97
- 4. Execute the command `git gc` to run the garbage collection
98
- 5. Restart oxidized - you're done!
99
-
100
91
  ## Oxidized ignores the changes I made to its git repository
101
92
  First of all: you shouldn't manipulate the git repository of oxidized. Don't
102
93
  create it, don't modify it, leave it alone. You can break things. You have
data/extra/device2yaml.rb CHANGED
@@ -6,24 +6,21 @@ require 'optparse'
6
6
  require 'etc'
7
7
  require 'timeout'
8
8
 
9
- # This scripts logs in a network device and outputs a yaml file that can be
9
+ # This script logs in a network device and outputs a YAML file that can be
10
10
  # used for model unit tests in spec/model/
11
11
  # For more information, see docs/DeviceSimulation.md
12
12
 
13
- # This script is quick & dirty - it grew with the time an could be a project
14
- # for its own. It works, and that should be enough ;-)
15
-
16
13
  ################# Methods
17
14
  # Runs cmd in the ssh session, either im exec mode or with a tty
18
15
  # saves the output to @output
19
16
  def ssh_exec(cmd)
20
- puts "\n### Sending #{cmd}..."
21
- @output&.puts " #{cmd}: |-"
17
+ puts "\n### Sending #{cmd.dump}..."
18
+ @output&.puts " #{@sequence_prepend_command}#{cmd.dump}: |-"
22
19
 
23
20
  if @exec_mode
24
- @ssh_output = @ssh.exec! cmd + "\n"
21
+ @ssh_output = @ssh.exec! cmd
25
22
  else
26
- @ses.send_data cmd + "\n"
23
+ @ses.send_data cmd
27
24
  shell_wait
28
25
  end
29
26
  yaml_output(' ')
@@ -68,7 +65,14 @@ def shell_wait
68
65
  puts "\n### ESC pressed, skipping idle timeout"
69
66
  return false
70
67
  else
71
- # if not, send the char through ssh
68
+ # if not, record the char and send the char through ssh
69
+ puts "\n### #{char.dump} pressed"
70
+ yaml_output(' ')
71
+ @output&.puts " #{@sequence_prepend_command}#{char.dump}: |-"
72
+ @ssh_output = ''
73
+ start_time = Time.now
74
+ @ssh_output_length = @ssh_output.length
75
+
72
76
  @ses.send_data char
73
77
  end
74
78
  end
@@ -85,6 +89,8 @@ def yaml_output(prepend = '')
85
89
  # Now print the collected output to @output
86
90
  firstline = true
87
91
 
92
+ prepend = @sequence_prepend_output + prepend
93
+
88
94
  # as we want to prepend 'prepend' to each line, we need each_line and chomp
89
95
  # chomp removes the trainling \n
90
96
  @ssh_output.each_line(chomp: true) do |line|
@@ -113,6 +119,9 @@ end
113
119
 
114
120
  # Define options
115
121
  options = {}
122
+ @sequence_prepend_command = '- '
123
+ @sequence_prepend_output = ' '
124
+
116
125
  optparse = OptionParser.new do |opts|
117
126
  opts.banner = <<~HEREDOC
118
127
  Usages:
@@ -140,6 +149,10 @@ optparse = OptionParser.new do |opts|
140
149
  options[:timeout] = timeout
141
150
  end
142
151
  opts.on('-e', '--exec-mode', 'Run ssh in exec mode (without tty)') { @exec_mode = true }
152
+ opts.on('-u', '--unordered', 'The YAML simulation should not enforce an order of the commands') do
153
+ @sequence_prepend_command = ''
154
+ @sequence_prepend_output = ''
155
+ end
143
156
  opts.on '-h', '--help', 'Print this help' do
144
157
  puts opts
145
158
  exit
@@ -182,6 +195,8 @@ elsif options[:input]
182
195
  end
183
196
 
184
197
  puts "Running #{ssh_commands} on #{ssh_user}@#{ssh_host}"
198
+ # Add \n to each command
199
+ ssh_commands.map! { |s| s + "\n" }
185
200
 
186
201
  # Defaut idle timeout: 5 seconds, as tests showed that 2 seconds is too short
187
202
  @idle_timeout = options[:timeout] || 5
data/extra/rest_client.rb CHANGED
@@ -6,7 +6,7 @@ module Oxidized
6
6
  require 'asetus'
7
7
 
8
8
  class Config
9
- Root = ENV['OXIDIZED_HOME'] || File.join(Dir.home, '.config', 'oxidized')
9
+ ROOT = ENV['OXIDIZED_HOME'] || File.join(Dir.home, '.config', 'oxidized')
10
10
  end
11
11
 
12
12
  CFGS = Asetus.new name: 'oxidized', load: false, key_to_s: true
@@ -38,7 +38,8 @@ module Oxidized
38
38
 
39
39
  def next(opt)
40
40
  data = JSON.dump opt
41
- @web.put PATH + '/node/next/' + opt[:name].to_s, data
41
+ headers = { 'content-type': 'application/json' }
42
+ @web.put PATH + '/node/next/' + opt[:name].to_s, data, headers
42
43
  end
43
44
  end
44
45
  end
data/extra/syslog.rb CHANGED
@@ -8,8 +8,10 @@
8
8
  # set system syslog host SERVER interactive-commands notice
9
9
  # set system syslog host SERVER match "^mgd\[[0-9]+\]: UI_COMMIT: .*"
10
10
 
11
- # Ports < 1024 need extra privileges, use a port higher than this by setting the port option in your oxidized config file.
12
- # To use the default port for syslog (514) you shouldn't pass an argument, but you will need to allow this with:
11
+ # Ports < 1024 need extra privileges, use a port higher than this by setting the
12
+ # port option in your oxidized config file.
13
+ # To use the default port for syslog (514) you shouldn't pass an argument, but
14
+ # you will need to allow this with:
13
15
  # sudo setcap 'cap_net_bind_service=+ep' /usr/bin/ruby
14
16
 
15
17
  # Config options are:
@@ -52,6 +54,7 @@ module Oxidized
52
54
  class SyslogMonitor
53
55
  MSG = {
54
56
  ios: /%SYS-(SW[0-9]+-)?5-CONFIG_I:/,
57
+ iosxr: /%MGBL-SYS-5-CONFIG_I/,
55
58
  junos: 'UI_COMMIT:',
56
59
  eos: /%SYS-5-CONFIG_I:/,
57
60
  nxos: /%VSHD-5-VSHD_SYSLOG_CONFIG_I:/,
@@ -89,6 +92,7 @@ module Oxidized
89
92
  opts[:from] = log[-1][1..-2]
90
93
  opts
91
94
  end
95
+ alias iosxr ios
92
96
  alias nxos ios
93
97
  alias eos ios
94
98
 
@@ -116,7 +120,8 @@ module Oxidized
116
120
  def run(io)
117
121
  loop do
118
122
  log = select [io]
119
- log, ip = log.first.first, nil
123
+ log = log.first.first
124
+ ip = nil
120
125
  if @mode == :udp
121
126
  log, ip = log.recvfrom_nonblock 2000
122
127
  ip = ip.last
data/lib/oxidized/cli.rb CHANGED
@@ -1,5 +1,9 @@
1
+ require 'semantic_logger'
2
+
1
3
  module Oxidized
2
4
  class CLI
5
+ include SemanticLogger::Loggable
6
+
3
7
  require 'slop'
4
8
  require 'oxidized'
5
9
  require 'English'
@@ -9,7 +13,7 @@ module Oxidized
9
13
  Process.daemon if @opts[:daemonize]
10
14
  write_pid
11
15
  begin
12
- Oxidized.logger.info "Oxidized starting, running as pid #{$PROCESS_ID}"
16
+ logger.info "Oxidized starting, running as pid #{$PROCESS_ID}"
13
17
  Oxidized.new
14
18
  rescue StandardError => e
15
19
  crash e
@@ -23,13 +27,13 @@ module Oxidized
23
27
  _args, @opts = parse_opts
24
28
 
25
29
  Config.load(@opts)
26
- Oxidized.setup_logger
30
+ Oxidized::Logger.setup
27
31
 
28
32
  @pidfile = File.expand_path(Oxidized.config.pid)
29
33
  end
30
34
 
31
35
  def crash(error)
32
- Oxidized.logger.fatal "Oxidized crashed, crashfile written in #{Config::CRASH}"
36
+ logger.fatal "Oxidized crashed, crashfile written in #{Config::CRASH}"
33
37
  File.open Config::CRASH, 'w' do |file|
34
38
  file.puts '-' * 50
35
39
  file.puts Time.now.utc
@@ -1,17 +1,25 @@
1
- module Oxidized::Config::Vars
2
- # convenience method for accessing node, group or global level user variables
3
- def vars(name)
4
- model_name = @node.model.class.name.to_s.downcase
5
- if @node.vars&.has_key?(name)
6
- @node.vars[name]
7
- elsif Oxidized.config.groups.has_key?(@node.group) && Oxidized.config.groups[@node.group].models.has_key(model_name) && Oxidized.config.groups[@node.group].models[model_name].vars.has_key?(name.to_s)
8
- Oxidized.config.groups[@node.group].models[model_name].vars[name.to_s]
9
- elsif Oxidized.config.groups.has_key?(@node.group) && Oxidized.config.groups[@node.group].vars.has_key?(name.to_s)
10
- Oxidized.config.groups[@node.group].vars[name.to_s]
11
- elsif Oxidized.config.models.has_key(model_name) && Oxidized.config.models[model_name].vars.has_key?(name.to_s)
12
- Oxidized.config.models[model_name].vars[name.to_s]
13
- elsif Oxidized.config.vars.has_key?(name.to_s)
14
- Oxidized.config.vars[name.to_s]
1
+ module Oxidized
2
+ class Config
3
+ module Vars
4
+ # convenience method for accessing node, group or global level user variables
5
+ def vars(name)
6
+ model_name = @node.model.class.name.to_s.downcase
7
+ if @node.vars&.has_key?(name)
8
+ @node.vars[name]
9
+ elsif Oxidized.config.groups.has_key?(@node.group) &&
10
+ Oxidized.config.groups[@node.group].models.has_key(model_name) &&
11
+ Oxidized.config.groups[@node.group].models[model_name].vars.has_key?(name.to_s)
12
+ Oxidized.config.groups[@node.group].models[model_name].vars[name.to_s]
13
+ elsif Oxidized.config.groups.has_key?(@node.group) &&
14
+ Oxidized.config.groups[@node.group].vars.has_key?(name.to_s)
15
+ Oxidized.config.groups[@node.group].vars[name.to_s]
16
+ elsif Oxidized.config.models.has_key(model_name) &&
17
+ Oxidized.config.models[model_name].vars.has_key?(name.to_s)
18
+ Oxidized.config.models[model_name].vars[name.to_s]
19
+ elsif Oxidized.config.vars.has_key?(name.to_s)
20
+ Oxidized.config.vars[name.to_s]
21
+ end
22
+ end
15
23
  end
16
24
  end
17
25
  end
@@ -27,7 +27,6 @@ module Oxidized
27
27
  asetus.default.model = 'junos'
28
28
  asetus.default.resolve_dns = true # if false, don't resolve DNS to IP
29
29
  asetus.default.interval = 3600
30
- asetus.default.use_syslog = false
31
30
  asetus.default.debug = false
32
31
  asetus.default.run_once = false
33
32
  asetus.default.threads = 30
@@ -35,7 +34,6 @@ module Oxidized
35
34
  asetus.default.timeout = 20
36
35
  asetus.default.retries = 3
37
36
  asetus.default.prompt = /^([\w.@-]+[#>]\s?)$/
38
- asetus.default.rest = '127.0.0.1:8888' # or false to disable
39
37
  asetus.default.next_adds_job = false # if true, /next adds job, so device is fetched immmeiately
40
38
  asetus.default.vars = {} # could be 'enable'=>'enablePW'
41
39
  asetus.default.groups = {} # group level configuration
@@ -43,6 +41,9 @@ module Oxidized
43
41
  asetus.default.models = {} # model level configuration
44
42
  asetus.default.pid = File.join(Oxidized::Config::ROOT, 'pid')
45
43
 
44
+ # Extentions
45
+ asetus.default.extensions['oxidized-web'].load = false
46
+
46
47
  asetus.default.crash.directory = File.join(Oxidized::Config::ROOT, 'crashes')
47
48
  asetus.default.crash.hostnames = false
48
49
 
data/lib/oxidized/core.rb CHANGED
@@ -6,6 +6,8 @@ module Oxidized
6
6
  end
7
7
 
8
8
  class Core
9
+ include SemanticLogger::Loggable
10
+
9
11
  class NoNodesFound < OxidizedError; end
10
12
 
11
13
  def initialize(_args)
@@ -23,15 +25,36 @@ module Oxidized
23
25
  end
24
26
  Signals.register_signal('HUP', reload_proc)
25
27
 
26
- # Initialize REST API and webUI if requested
27
- if Oxidized.config.rest?
28
+ # Load extensions, currently only oxidized-web
29
+ # We have different namespaces for oxidized-web, which needs to be
30
+ # adressed if we need a generic way to load extensions:
31
+ # - gem: oxidized-web
32
+ # - module: Oxidized::API
33
+ # - path: oxidized/web
34
+ # - entrypoint: Oxidized::API::Web.new(nodes, configuration)
35
+
36
+ # Initialize oxidized-web if requested
37
+ if Oxidized.config.has_key? 'rest'
38
+ logger.warn(
39
+ 'configuration: "rest" is deprecated. Migrate to ' \
40
+ '"extensions.oxidized-web" and remove "rest" from the configuration'
41
+ )
42
+ configuration = Oxidized.config.rest
43
+ elsif Oxidized.config.extensions['oxidized-web'].load?
44
+ # This comment stops rubocop complaining about Style/IfUnlessModifier
45
+ configuration = Oxidized.config.extensions['oxidized-web']
46
+ end
47
+
48
+ if configuration
28
49
  begin
29
50
  require 'oxidized/web'
30
51
  rescue LoadError
31
- raise OxidizedError, 'oxidized-web not found: sudo gem install oxidized-web - \
32
- or disable web support by setting "rest: false" in your configuration'
52
+ raise OxidizedError,
53
+ 'oxidized-web not found: install it or disable it by ' \
54
+ 'removing "rest" and "extensions.oxidized-web" from your ' \
55
+ 'configuration'
33
56
  end
34
- @rest = API::Web.new nodes, Oxidized.config.rest
57
+ @rest = API::Web.new nodes, configuration
35
58
  @rest.run
36
59
  end
37
60
  run
@@ -40,14 +63,13 @@ module Oxidized
40
63
  private
41
64
 
42
65
  def reload
43
- Oxidized.logger.info("Reloading node list and log files")
66
+ logger.info("Reloading node list")
44
67
  @worker.reload
45
- Oxidized.logger.reopen
46
68
  @need_reload = false
47
69
  end
48
70
 
49
71
  def run
50
- Oxidized.logger.debug "lib/oxidized/core.rb: Starting the worker..."
72
+ logger.debug "Starting the worker..."
51
73
  loop do
52
74
  reload if @need_reload
53
75
  @worker.work
@@ -14,30 +14,32 @@ class CiscoSparkDiff < Oxidized::Hook
14
14
  return unless ctx.node
15
15
  return unless ctx.event.to_s == "post_store"
16
16
 
17
- log "Connecting to Cisco Spark"
17
+ logger.info "Connecting to Cisco Spark"
18
18
  CiscoSpark.configure do |config|
19
19
  config.api_key = cfg.accesskey
20
20
  config.proxy = cfg.proxy if cfg.has_key?('proxy')
21
21
  end
22
22
  room = CiscoSpark::Room.new(id: cfg.space)
23
- log "Connected"
23
+ logger.info "Connected"
24
24
 
25
25
  if cfg.has_key?("diff") ? cfg.diff : true
26
26
  gitoutput = ctx.node.output.new
27
27
  diff = gitoutput.get_diff ctx.node, ctx.node.group, ctx.commitref, nil
28
28
  title = ctx.node.name.to_s
29
- log "Posting diff as snippet to #{cfg.space}"
30
- room.send_message CiscoSpark::Message.new(text: 'Device ' + title + ' modified:' + "\n" + diff[:patch].lines.to_a[4..-1].join)
29
+ logger.info "Posting diff as snippet to #{cfg.space}"
30
+ room.send_message CiscoSpark::Message.new(text: "Device #{title} modified:\n" +
31
+ diff[:patch].lines.to_a[4..-1].join)
31
32
  end
32
33
 
33
34
  if cfg.message?
34
- log cfg.message
35
- msg = cfg.message % { node: ctx.node.name.to_s, group: ctx.node.group.to_s, commitref: ctx.commitref, model: ctx.node.model.class.name.to_s.downcase }
36
- log msg
37
- log "Posting message to #{cfg.space}"
35
+ logger.info cfg.message
36
+ msg = cfg.message % { node: ctx.node.name.to_s, group: ctx.node.group.to_s, commitref: ctx.commitref,
37
+ model: ctx.node.model.class.name.to_s.downcase }
38
+ logger.info msg
39
+ logger.info "Posting message to #{cfg.space}"
38
40
  room.send_message CiscoSpark::Message.new(text: msg)
39
41
  end
40
42
 
41
- log "Finished"
43
+ logger.info "Finished"
42
44
  end
43
45
  end
@@ -28,7 +28,7 @@ class Exec < Oxidized::Hook
28
28
 
29
29
  def run_hook(ctx)
30
30
  env = make_env ctx
31
- log "Execute: #{@cmd.inspect}", :debug
31
+ logger.debug "Execute: #{@cmd.inspect}"
32
32
  th = Thread.new do
33
33
  run_cmd! env
34
34
  rescue StandardError => e
@@ -38,20 +38,21 @@ class Exec < Oxidized::Hook
38
38
  end
39
39
 
40
40
  def run_cmd!(env)
41
- pid, status = nil, nil
41
+ pid = nil
42
+ status = nil
42
43
  Timeout.timeout(@timeout) do
43
44
  pid = spawn env, @cmd, unsetenv_others: true
44
45
  pid, status = wait2 pid
45
46
  unless status.exitstatus.zero?
46
47
  msg = "#{@cmd.inspect} failed with exit value #{status.exitstatus}"
47
- log msg, :error
48
+ logger.error msg
48
49
  raise msg
49
50
  end
50
51
  end
51
52
  rescue Timeout::Error
52
53
  kill "TERM", pid
53
54
  msg = "#{@cmd} timed out"
54
- log msg, :error
55
+ logger.error msg
55
56
  raise Timeout::Error, msg
56
57
  end
57
58
 
@@ -6,8 +6,13 @@ class GithubRepo < Oxidized::Hook
6
6
  end
7
7
 
8
8
  def run_hook(ctx)
9
+ unless ctx.node
10
+ logger.error 'GithubRepo.run_hook: no node provided'
11
+ return
12
+ end
13
+
9
14
  unless ctx.node.repo
10
- log "Oxidized output is not git, can't push to remote", :error
15
+ logger.error "Oxidized output is not git, can't push to remote"
11
16
  return
12
17
  end
13
18
  repo = Rugged::Repository.new(ctx.node.repo)
@@ -15,12 +20,11 @@ class GithubRepo < Oxidized::Hook
15
20
  url = remote_repo(ctx.node)
16
21
 
17
22
  if url.nil? || url.empty?
18
- log "No repository defined for #{ctx.node.group}/#{ctx.node.name}", :error
23
+ logger.error "No repository defined for #{ctx.node.group}/#{ctx.node.name}"
19
24
  return
20
25
  end
21
26
 
22
- log "Pushing local repository(#{repo.path})..."
23
- log "to remote: #{url}"
27
+ logger.info "Pushing local repository(#{repo.path}) to remote: #{url}"
24
28
 
25
29
  if repo.remotes['origin'].nil?
26
30
  repo.remotes.create('origin', url)
@@ -34,10 +38,10 @@ class GithubRepo < Oxidized::Hook
34
38
  remote.push([repo.head.name], credentials: creds)
35
39
  rescue Rugged::NetworkError => e
36
40
  if e.message == 'unsupported URL protocol'
37
- log "Rugged does not support the git URL '#{url}'.", :warn
41
+ logger.warn "Rugged does not support the git URL '#{url}'."
38
42
  unless Rugged.features.include?(:ssh)
39
- log 'You may need to install Rugged with ssh support ' \
40
- '(gem install rugged -- --with-ssh)', :warn
43
+ logger.warn "Note: Rugged isn't installed with ssh support. You may need " \
44
+ '"gem install rugged -- --with-ssh"'
41
45
  end
42
46
  end
43
47
  # re-raise exception for the calling method
@@ -47,28 +51,28 @@ class GithubRepo < Oxidized::Hook
47
51
 
48
52
  def fetch_and_merge_remote(repo, creds)
49
53
  result = repo.fetch('origin', [repo.head.name], credentials: creds)
50
- log result.inspect, :debug
54
+ logger.debug result.inspect
51
55
 
52
56
  their_branch = remote_branch(repo)
53
57
 
54
58
  unless their_branch
55
- log 'remote branch does not exist yet, nothing to merge', :debug
59
+ logger.debug 'remote branch does not exist yet, nothing to merge'
56
60
  return
57
61
  end
58
62
 
59
63
  result = repo.merge_analysis(their_branch.target_id)
60
64
 
61
65
  if result.include? :up_to_date
62
- log 'nothing to merge', :debug
66
+ logger.debug 'nothing to merge'
63
67
  return
64
68
  end
65
69
 
66
- log "merging fetched branch #{their_branch.name}", :debug
70
+ logger.debug "merging fetched branch #{their_branch.name}"
67
71
 
68
72
  merge_index = repo.merge_commits(repo.head.target_id, their_branch.target_id)
69
73
 
70
74
  if merge_index.conflicts?
71
- log("Conflicts detected, skipping Rugged::Commit.create", :warn)
75
+ logger.warn "Conflicts detected, skipping Rugged::Commit.create"
72
76
  return
73
77
  end
74
78
 
@@ -85,18 +89,20 @@ class GithubRepo < Oxidized::Hook
85
89
  Proc.new do |_url, username_from_url, _allowed_types| # rubocop:disable Style/Proc
86
90
  git_user = cfg.has_key?('username') ? cfg.username : (username_from_url || 'git')
87
91
  if cfg.has_key?('password')
88
- log "Authenticating using username and password as '#{git_user}'", :debug
92
+ logger.debug "Authenticating using username and password as '#{git_user}'"
89
93
  Rugged::Credentials::UserPassword.new(username: git_user, password: cfg.password)
90
94
  elsif cfg.has_key?('privatekey')
91
95
  pubkey = cfg.has_key?('publickey') ? cfg.publickey : nil
92
- log "Authenticating using ssh keys as '#{git_user}'", :debug
96
+ logger.debug "Authenticating using ssh keys as '#{git_user}'"
93
97
  rugged_sshkey(git_user: git_user, privkey: cfg.privatekey, pubkey: pubkey)
94
- elsif cfg.has_key?('remote_repo') && cfg.remote_repo.has_key?(node.group) && cfg.remote_repo[node.group].has_key?('privatekey')
98
+ elsif cfg.has_key?('remote_repo') &&
99
+ cfg.remote_repo.has_key?(node.group) &&
100
+ cfg.remote_repo[node.group].has_key?('privatekey')
95
101
  pubkey = cfg.remote_repo[node.group].has_key?('publickey') ? cfg.remote_repo[node.group].publickey : nil
96
- log "Authenticating using ssh keys as '#{git_user}' for '#{node.group}/#{node.name}'", :debug
102
+ logger.debug "Authenticating using ssh keys as '#{git_user}' for '#{node.group}/#{node.name}'"
97
103
  rugged_sshkey(git_user: git_user, privkey: cfg.remote_repo[node.group].privatekey, pubkey: pubkey)
98
104
  else
99
- log "Authenticating using ssh agent as '#{git_user}'", :debug
105
+ logger.debug "Authenticating using ssh agent as '#{git_user}'"
100
106
  Rugged::Credentials::SshKeyFromAgent.new(username: git_user)
101
107
  end
102
108
  end
@@ -1,9 +1,9 @@
1
1
  class NoopHook < Oxidized::Hook
2
2
  def validate_cfg!
3
- log "Validate config"
3
+ logger.info "Validate config"
4
4
  end
5
5
 
6
6
  def run_hook(ctx)
7
- log "Run hook with context: #{ctx}"
7
+ logger.info "Run hook with context: #{ctx}"
8
8
  end
9
9
  end
@@ -12,7 +12,7 @@ class SlackDiff < Oxidized::Hook
12
12
  end
13
13
 
14
14
  def slack_upload(client, title, content, channel)
15
- log "Posting diff as snippet to #{channel}"
15
+ logger.info "Posting diff as snippet to #{channel}"
16
16
  upload_dest = client.files_getUploadURLExternal(filename: "change",
17
17
  length: content.length,
18
18
  snippet_type: "diff")
@@ -39,14 +39,14 @@ class SlackDiff < Oxidized::Hook
39
39
  return unless ctx.node
40
40
  return unless ctx.event.to_s == "post_store"
41
41
 
42
- log "Connecting to slack"
42
+ logger.info "Connecting to slack"
43
43
  Slack::Web::Client.configure do |config|
44
44
  config.token = cfg.token
45
45
  config.proxy = cfg.proxy if cfg.has_key?('proxy')
46
46
  end
47
47
  client = Slack::Web::Client.new
48
48
  client.auth_test
49
- log "Connected"
49
+ logger.info "Connected"
50
50
  if cfg.has_key?("diff") ? cfg.diff : true
51
51
  gitoutput = ctx.node.output.new
52
52
  diff = gitoutput.get_diff ctx.node, ctx.node.group, ctx.commitref, nil
@@ -58,12 +58,13 @@ class SlackDiff < Oxidized::Hook
58
58
  end
59
59
  # message custom formatted - optional
60
60
  if cfg.message?
61
- log cfg.message
62
- msg = cfg.message % { node: ctx.node.name.to_s, group: ctx.node.group.to_s, commitref: ctx.commitref, model: ctx.node.model.class.name.to_s.downcase }
63
- log msg
64
- log "Posting message to #{cfg.channel}"
61
+ logger.info cfg.message
62
+ msg = cfg.message % { node: ctx.node.name.to_s, group: ctx.node.group.to_s, commitref: ctx.commitref,
63
+ model: ctx.node.model.class.name.to_s.downcase }
64
+ logger.info msg
65
+ logger.info "Posting message to #{cfg.channel}"
65
66
  client.chat_postMessage(channel: cfg.channel, text: msg, as_user: true)
66
67
  end
67
- log "Finished"
68
+ logger.info "Finished"
68
69
  end
69
70
  end