oxidized 0.29.1 → 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 (236) hide show
  1. checksums.yaml +4 -4
  2. data/.coderabbit.yaml +21 -0
  3. data/.github/ISSUE_TEMPLATE/bug_report.md +45 -0
  4. data/.github/ISSUE_TEMPLATE/feature_request.md +22 -0
  5. data/.github/ISSUE_TEMPLATE/support-request.md +39 -0
  6. data/.github/workflows/codeql.yml +4 -4
  7. data/.github/workflows/publishdocker.yml +108 -13
  8. data/.github/workflows/ruby.yml +3 -12
  9. data/.github/workflows/stale.yml +6 -1
  10. data/.gitignore +2 -0
  11. data/.rubocop.yml +69 -13
  12. data/.rubocop_todo.yml +13 -143
  13. data/CHANGELOG.md +431 -4
  14. data/CONTRIBUTING.md +189 -0
  15. data/Dockerfile +89 -34
  16. data/README.md +59 -188
  17. data/Rakefile +58 -12
  18. data/bin/oxidized +2 -5
  19. data/docs/Configuration.md +382 -149
  20. data/docs/Creating-Models.md +262 -10
  21. data/docs/DeviceSimulation.md +196 -0
  22. data/docs/Docker.md +246 -0
  23. data/docs/Hooks.md +146 -51
  24. data/docs/Inputs.md +237 -0
  25. data/docs/Issues.md +123 -0
  26. data/docs/Model-Notes/APC.md +72 -0
  27. data/docs/Model-Notes/Cumulus.md +5 -0
  28. data/docs/Model-Notes/EatonNetwork.md +18 -0
  29. data/docs/Model-Notes/ExaLink.md +43 -0
  30. data/docs/Model-Notes/FSOS.md +17 -0
  31. data/docs/Model-Notes/Fortinet.md +75 -0
  32. data/docs/Model-Notes/GrandstreamHT8xx.md +8 -0
  33. data/docs/Model-Notes/HPEAruba.md +32 -0
  34. data/docs/Model-Notes/IvantiConnectSecure.md +59 -0
  35. data/docs/Model-Notes/OS6.md +10 -0
  36. data/docs/Model-Notes/README.md +1 -20
  37. data/docs/Model-Notes/RouterOS.md +28 -0
  38. data/docs/Model-Notes/SikluMHTG.md +7 -0
  39. data/docs/Model-Notes/TrueNAS.md +23 -0
  40. data/docs/Model-Notes/XGS4600-Zyxel.md +5 -0
  41. data/docs/ModelUnitTests.md +219 -0
  42. data/docs/Outputs.md +102 -5
  43. data/docs/Release.md +102 -0
  44. data/docs/Ruby-API.md +135 -2
  45. data/docs/Sources.md +34 -0
  46. data/docs/Supported-OS-Types.md +207 -270
  47. data/docs/Troubleshooting.md +43 -1
  48. data/extra/device2yaml.rb +259 -0
  49. data/extra/gitdiff-msteams.sh +32 -5
  50. data/extra/hooks/modelrules.rb +55 -0
  51. data/extra/hooks/modelrulesadvanced.rb +167 -0
  52. data/extra/hooks/srcipmap.rb +54 -0
  53. data/extra/nagios_check_failing_nodes.rb +1 -1
  54. data/extra/oxidized.runit +1 -1
  55. data/extra/rest_client.rb +5 -4
  56. data/extra/syslog.rb +11 -6
  57. data/lib/oxidized/cli/support.rb +152 -0
  58. data/lib/oxidized/cli.rb +21 -6
  59. data/lib/oxidized/config/vars.rb +33 -14
  60. data/lib/oxidized/config.rb +28 -17
  61. data/lib/oxidized/core.rb +50 -8
  62. data/lib/oxidized/hook/ciscosparkdiff.rb +11 -9
  63. data/lib/oxidized/hook/exec.rb +5 -4
  64. data/lib/oxidized/hook/githubrepo.rb +55 -18
  65. data/lib/oxidized/hook/noophook.rb +2 -2
  66. data/lib/oxidized/hook/slackdiff.rb +50 -14
  67. data/lib/oxidized/hook/xmppdiff.rb +9 -9
  68. data/lib/oxidized/hook.rb +68 -16
  69. data/lib/oxidized/input/cli.rb +8 -3
  70. data/lib/oxidized/input/debugtext.rb +40 -0
  71. data/lib/oxidized/input/debugyaml.rb +82 -0
  72. data/lib/oxidized/input/exec.rb +2 -11
  73. data/lib/oxidized/input/ftp.rb +2 -19
  74. data/lib/oxidized/input/http.rb +67 -17
  75. data/lib/oxidized/input/input.rb +35 -14
  76. data/lib/oxidized/input/scp.rb +27 -0
  77. data/lib/oxidized/input/ssh.rb +59 -83
  78. data/lib/oxidized/input/sshbase.rb +102 -0
  79. data/lib/oxidized/input/telnet.rb +20 -19
  80. data/lib/oxidized/input/tftp.rb +8 -8
  81. data/lib/oxidized/job.rb +18 -7
  82. data/lib/oxidized/jobs.rb +11 -1
  83. data/lib/oxidized/logger.rb +51 -0
  84. data/lib/oxidized/manager.rb +18 -17
  85. data/lib/oxidized/model/acos.rb +2 -1
  86. data/lib/oxidized/model/addpack.rb +26 -0
  87. data/lib/oxidized/model/adtran.rb +5 -1
  88. data/lib/oxidized/model/adva.rb +3 -3
  89. data/lib/oxidized/model/airfiber.rb +1 -1
  90. data/lib/oxidized/model/aos7.rb +11 -0
  91. data/lib/oxidized/model/aoscx.rb +37 -29
  92. data/lib/oxidized/model/aosw.rb +39 -29
  93. data/lib/oxidized/model/apc_aos.rb +7 -2
  94. data/lib/oxidized/model/apcaos.rb +39 -0
  95. data/lib/oxidized/model/aricentiss.rb +9 -2
  96. data/lib/oxidized/model/arubainstant.rb +93 -0
  97. data/lib/oxidized/model/asa.rb +11 -10
  98. data/lib/oxidized/model/asternos.rb +22 -0
  99. data/lib/oxidized/model/asyncos.rb +3 -3
  100. data/lib/oxidized/model/audiocodes.rb +2 -2
  101. data/lib/oxidized/model/awplus.rb +15 -12
  102. data/lib/oxidized/model/axos.rb +1 -1
  103. data/lib/oxidized/model/bdcom.rb +1 -0
  104. data/lib/oxidized/model/c4cmts.rb +1 -2
  105. data/lib/oxidized/model/ciscosma.rb +1 -1
  106. data/lib/oxidized/model/ciscosmb.rb +6 -1
  107. data/lib/oxidized/model/cnos.rb +13 -10
  108. data/lib/oxidized/model/comtrol.rb +1 -1
  109. data/lib/oxidized/model/comware.rb +5 -3
  110. data/lib/oxidized/model/cumulus.rb +14 -2
  111. data/lib/oxidized/model/defacto.rb +26 -0
  112. data/lib/oxidized/model/dellx.rb +1 -1
  113. data/lib/oxidized/model/dlink.rb +5 -2
  114. data/lib/oxidized/model/dlinknextgen.rb +55 -0
  115. data/lib/oxidized/model/dnos.rb +3 -0
  116. data/lib/oxidized/model/dslcommands.rb +93 -0
  117. data/lib/oxidized/model/dslsetup.rb +102 -0
  118. data/lib/oxidized/model/eatonnetwork.rb +70 -0
  119. data/lib/oxidized/model/edgecos.rb +3 -1
  120. data/lib/oxidized/model/edgeos.rb +7 -6
  121. data/lib/oxidized/model/edgeswitch.rb +3 -1
  122. data/lib/oxidized/model/efos.rb +41 -0
  123. data/lib/oxidized/model/eltex.rb +4 -2
  124. data/lib/oxidized/model/enterasys800.rb +2 -2
  125. data/lib/oxidized/model/enterprise_sonic.rb +46 -0
  126. data/lib/oxidized/model/eos.rb +3 -1
  127. data/lib/oxidized/model/exalink.rb +36 -0
  128. data/lib/oxidized/model/f5os.rb +17 -0
  129. data/lib/oxidized/model/fabricos.rb +2 -2
  130. data/lib/oxidized/model/fastiron.rb +5 -3
  131. data/lib/oxidized/model/firebrick.rb +2 -2
  132. data/lib/oxidized/model/firelinuxos.rb +13 -6
  133. data/lib/oxidized/model/firewareos.rb +10 -1
  134. data/lib/oxidized/model/fortigate.rb +160 -0
  135. data/lib/oxidized/model/fortios.rb +38 -48
  136. data/lib/oxidized/model/fsos.rb +46 -0
  137. data/lib/oxidized/model/gaiaos.rb +4 -4
  138. data/lib/oxidized/model/garderos.rb +38 -0
  139. data/lib/oxidized/model/grandstreamht8xx.rb +19 -0
  140. data/lib/oxidized/model/h3c.rb +1 -1
  141. data/lib/oxidized/model/ibos.rb +1 -0
  142. data/lib/oxidized/model/ingate.rb +47 -0
  143. data/lib/oxidized/model/ios.rb +57 -30
  144. data/lib/oxidized/model/iosxr.rb +2 -2
  145. data/lib/oxidized/model/ironware.rb +7 -5
  146. data/lib/oxidized/model/ivanti.rb +54 -0
  147. data/lib/oxidized/model/junos.rb +12 -5
  148. data/lib/oxidized/model/kornfeldos.rb +33 -0
  149. data/lib/oxidized/model/lenovonos.rb +2 -0
  150. data/lib/oxidized/model/linksyssrw.rb +3 -3
  151. data/lib/oxidized/model/linuxgeneric.rb +4 -2
  152. data/lib/oxidized/model/macros.rb +60 -0
  153. data/lib/oxidized/model/mimosab11.rb +34 -0
  154. data/lib/oxidized/model/ml66.rb +33 -0
  155. data/lib/oxidized/model/mlnxos.rb +18 -14
  156. data/lib/oxidized/model/model.rb +98 -103
  157. data/lib/oxidized/model/mtrlrfs.rb +1 -0
  158. data/lib/oxidized/model/ndms.rb +6 -0
  159. data/lib/oxidized/model/netgear.rb +14 -3
  160. data/lib/oxidized/model/netonix.rb +1 -1
  161. data/lib/oxidized/model/netscaler.rb +1 -1
  162. data/lib/oxidized/model/nodegrid.rb +1 -1
  163. data/lib/oxidized/model/nsxdfw.rb +31 -0
  164. data/lib/oxidized/model/nsxfirewall.rb +2 -1
  165. data/lib/oxidized/model/nxos.rb +20 -15
  166. data/lib/oxidized/model/ocnos.rb +42 -0
  167. data/lib/oxidized/model/onefinity.rb +18 -0
  168. data/lib/oxidized/model/openbsd.rb +1 -1
  169. data/lib/oxidized/model/opengear.rb +36 -1
  170. data/lib/oxidized/model/openwrt.rb +6 -6
  171. data/lib/oxidized/model/opnsense.rb +9 -1
  172. data/lib/oxidized/model/os6.rb +48 -0
  173. data/lib/oxidized/model/outputs.rb +5 -0
  174. data/lib/oxidized/model/panos.rb +2 -0
  175. data/lib/oxidized/model/perle.rb +41 -0
  176. data/lib/oxidized/model/pfsense.rb +9 -0
  177. data/lib/oxidized/model/powerconnect.rb +32 -11
  178. data/lib/oxidized/model/procurve.rb +6 -2
  179. data/lib/oxidized/model/qtech.rb +3 -1
  180. data/lib/oxidized/model/quantaos.rb +8 -6
  181. data/lib/oxidized/model/rgos.rb +33 -0
  182. data/lib/oxidized/model/riverbed.rb +104 -0
  183. data/lib/oxidized/model/routeros.rb +18 -11
  184. data/lib/oxidized/model/saos.rb +18 -1
  185. data/lib/oxidized/model/saos10.rb +38 -0
  186. data/lib/oxidized/model/siklu.rb +1 -1
  187. data/lib/oxidized/model/siklumhtg.rb +22 -0
  188. data/lib/oxidized/model/sixwind.rb +28 -0
  189. data/lib/oxidized/model/slxos.rb +3 -3
  190. data/lib/oxidized/model/smartbyte.rb +48 -0
  191. data/lib/oxidized/model/sonicos.rb +27 -20
  192. data/lib/oxidized/model/speedtouch.rb +1 -1
  193. data/lib/oxidized/model/sros.rb +3 -3
  194. data/lib/oxidized/model/srosmd.rb +1 -1
  195. data/lib/oxidized/model/supermicro.rb +1 -1
  196. data/lib/oxidized/model/telco.rb +1 -1
  197. data/lib/oxidized/model/tmos.rb +1 -0
  198. data/lib/oxidized/model/tnsr.rb +57 -0
  199. data/lib/oxidized/model/tplink.rb +5 -5
  200. data/lib/oxidized/model/trango.rb +4 -2
  201. data/lib/oxidized/model/truenas.rb +64 -3
  202. data/lib/oxidized/model/ucs.rb +1 -1
  203. data/lib/oxidized/model/unifiap.rb +144 -0
  204. data/lib/oxidized/model/uplinkolt.rb +46 -0
  205. data/lib/oxidized/model/voltaire.rb +1 -1
  206. data/lib/oxidized/model/voss.rb +3 -0
  207. data/lib/oxidized/model/vrp.rb +5 -1
  208. data/lib/oxidized/model/vyatta.rb +9 -3
  209. data/lib/oxidized/model/vyos.rb +37 -0
  210. data/lib/oxidized/model/xos.rb +10 -1
  211. data/lib/oxidized/model/zhoneolt.rb +4 -2
  212. data/lib/oxidized/model/zynos.rb +67 -3
  213. data/lib/oxidized/model/zynosadsl.rb +14 -0
  214. data/lib/oxidized/model/zynoscli.rb +1 -1
  215. data/lib/oxidized/model/zynosgs.rb +2 -0
  216. data/lib/oxidized/model/zynosmgs.rb +32 -0
  217. data/lib/oxidized/node.rb +99 -64
  218. data/lib/oxidized/nodes.rb +40 -19
  219. data/lib/oxidized/output/file.rb +80 -43
  220. data/lib/oxidized/output/git.rb +295 -151
  221. data/lib/oxidized/output/gitcrypt.rb +193 -185
  222. data/lib/oxidized/output/http.rb +63 -51
  223. data/lib/oxidized/output/output.rb +20 -4
  224. data/lib/oxidized/signals.rb +44 -0
  225. data/lib/oxidized/source/csv.rb +49 -49
  226. data/lib/oxidized/source/http.rb +76 -63
  227. data/lib/oxidized/source/jsonfile.rb +68 -0
  228. data/lib/oxidized/source/source.rb +73 -18
  229. data/lib/oxidized/source/sql.rb +71 -59
  230. data/lib/oxidized/version.rb +6 -4
  231. data/lib/oxidized/worker.rb +57 -29
  232. data/lib/oxidized.rb +4 -24
  233. data/lib/refinements.rb +21 -1
  234. data/oxidized.gemspec +45 -21
  235. metadata +248 -41
  236. data/lib/oxidized/model/timos.rb +0 -10
@@ -0,0 +1,37 @@
1
+ class Vyos < Oxidized::Model
2
+ using Refinements
3
+
4
+ # VyOS model #
5
+ # Vyos is a Fork of Vyatta and is being actively developed.
6
+ # https://vyos.org/
7
+
8
+ prompt /^\S+@\S+(:~\$|>) $/
9
+ clean :escape_codes
10
+
11
+ cmd :all do |cfg|
12
+ cfg.lines.to_a[1..-2].join
13
+ end
14
+
15
+ cmd :secret do |cfg|
16
+ cfg.gsub! /secret (\S+).*/, 'secret <secret removed>'
17
+ cfg.gsub! /password (\S+).*/, 'password <secret removed>'
18
+ cfg.gsub! /snmp community (\S+)/, 'snmp community <secret removed>'
19
+ cfg.gsub! /snmp trap-target ([^\s\\]*) community (\S+)/, 'snmp trap-target \1 community <secret removed>'
20
+ cfg.gsub! /preshared-key (\S+).*/, 'preshared-key <secret removed>'
21
+ cfg.gsub! /private key (\S+).*/, 'private key <secret removed>'
22
+ cfg.gsub! /private-key (\S+).*/, 'private-key <secret removed>'
23
+ # password in URLs like protocol://user:password@domain.tld/
24
+ cfg.gsub! /([a-z]+:\/\/[^:\s]+:)\S+@/, '\1<secret removed>@'
25
+ cfg
26
+ end
27
+
28
+ cmd 'show version' do |cfg|
29
+ comment cfg
30
+ end
31
+
32
+ cmd 'show configuration commands | no-more'
33
+
34
+ cfg :ssh do
35
+ pre_logout 'exit'
36
+ end
37
+ end
@@ -12,6 +12,13 @@ class XOS < Oxidized::Model
12
12
  cfg.each_line.to_a[1..-2].map { |line| line.delete("\r").rstrip }.join("\n") + "\n"
13
13
  end
14
14
 
15
+ cmd :secret do |cfg|
16
+ cfg.gsub! /^(configure (radius|radius-accounting) (netlogin|mgmt-access) (primary|secondary) shared-secret encrypted).+/, '\\1 <secret hidden>'
17
+ cfg.gsub! /^(configure account admin encrypted).+/, '\\1 <secret hidden>'
18
+ cfg.gsub! /^(create account (admin|user) (.+) encrypted).+/, '\\1 <secret hidden>'
19
+ cfg
20
+ end
21
+
15
22
  cmd 'show version' do |cfg|
16
23
  comment cfg
17
24
  end
@@ -26,7 +33,9 @@ class XOS < Oxidized::Model
26
33
 
27
34
  cmd 'show switch' do |cfg|
28
35
  cfg.gsub! /Next periodic save on.*/, ''
29
- comment cfg.each_line.reject { |line| line.match(/Time:/) || line.match(/boot/i) || line.match(/Next periodic/) }.join
36
+ comment cfg.each_line.reject { |line|
37
+ line.match(/Time:/) || line.match(/boot/i) || line.match(/Next periodic/)
38
+ }.join
30
39
  end
31
40
 
32
41
  cmd 'show configuration' do |cfg|
@@ -5,7 +5,7 @@ class ZhoneOLT < Oxidized::Model
5
5
 
6
6
  # the prompt can be anything on zhone, but it defaults to 'zXX>' and we
7
7
  # always use hostname>
8
- prompt /^(\r*[\w.@():-]+[>]\s?)$/
8
+ prompt /^(\r*[\w.@():-]+>\s?)$/
9
9
  comment '# '
10
10
 
11
11
  cmd :secret do |cfg|
@@ -36,7 +36,9 @@ class ZhoneOLT < Oxidized::Model
36
36
  end
37
37
 
38
38
  cmd 'ethrpshow' do |cfg|
39
- cfg = cfg.each_line.select { |line| line.match /Vendor (Name|OUI|Part|Revision)|Serial Number|Manufacturing Date/ }.join
39
+ cfg = cfg.each_line.select do |line|
40
+ line.match /Vendor (Name|OUI|Part|Revision)|Serial Number|Manufacturing Date/
41
+ end.join
40
42
  comment cfg
41
43
  end
42
44
 
@@ -1,12 +1,76 @@
1
1
  class ZyNOS < Oxidized::Model
2
2
  using Refinements
3
3
 
4
- # Used in Zyxel DSLAMs, such as SAM1316
4
+ prompt /^([\w.@()\-<]+[#>]\s?)$/
5
+ # if there is something you can not identify after prompt, uncomment next line and comment previous line
6
+ # prompt /^([\w.@()\-<]+[#>]\s?).*$/
5
7
 
6
8
  comment '! '
7
9
 
8
- cmd 'config-0'
10
+ # Used in Zyxel DSLAMs, such as SAM1316. Uncomment next line to enable ftp.
11
+ # cmd 'config-0'
9
12
 
10
- cfg :ftp do
13
+ # replace next line control sequence with a new line
14
+ expect /(\e\[1M\e\[\??\d+(;\d+)*[A-Za-z]\e\[1L)|(\eE)/ do |data, re|
15
+ data.gsub re, "\n"
16
+ end
17
+
18
+ # replace all used vt100 control sequences
19
+ expect /\e\[\??\d+(;\d+)*[A-Za-z]/ do |data, re|
20
+ data.gsub re, ''
21
+ end
22
+
23
+ # ignore copyright motd
24
+ expect /^(Copyright .*)\n^([\w.@()\-<]+[#>]\s?)$/ do
25
+ send '\n'
26
+ ""
27
+ end
28
+
29
+ cmd :all do |cfg|
30
+ cfg = cfg.gsub /^\r/, ''
31
+ # Additional filtering for elder switches sending vt100 control chars via telnet
32
+ cfg.gsub! /\e\[\??\d+(;\d+)*[A-Za-z]/, ''
33
+ cfg
34
+ end
35
+
36
+ # remove snmp community, username, password and admin-password
37
+ cmd :secret do |cfg|
38
+ cfg.gsub! /^(snmp-server get-community) \S+(.*)/, '\\1 <secret hidden> \\2'
39
+ cfg.gsub! /^(snmp-server set-community) \S+(.*)/, '\\1 <secret hidden> \\2'
40
+ cfg.gsub! /^(logins username) \S+(.*) (password) \S+(.*)/, '\\1 <secret hidden> \\2 \\3 <secret hidden> \\4'
41
+ cfg.gsub! /^(admin-password) \S+(.*)/, '\\1 <secret hidden> \\2'
42
+ cfg.gsub! /^(password) \S+(.*) (privilege \S+)/, '\\1 <secret hidden> \\2 \\3'
43
+ cfg
44
+ end
45
+
46
+ cmd 'show version' do |cfg|
47
+ comment cfg
48
+ end
49
+
50
+ cmd 'show system-information' do |cfg|
51
+ cfg.gsub! /^([Ss]ystem up [Tt]ime\s*:)(.*)/, '\\1 <time removed>'
52
+ comment cfg
53
+ end
54
+
55
+ cmd 'show running-config' do |cfg|
56
+ cfg = cfg.split("\n")[4..-2].join("\n")
57
+ cfg
58
+ end
59
+
60
+ cfg :telnet do
61
+ username /^User name:/i
62
+ password /^Password:/i
63
+ end
64
+
65
+ cfg :telnet, :ssh do
66
+ post_login do
67
+ if vars(:enable) == true
68
+ cmd "enable"
69
+ elsif vars(:enable)
70
+ cmd "enable", /^[pP]assword:/
71
+ cmd vars(:enable)
72
+ end
73
+ end
74
+ pre_logout 'exit'
11
75
  end
12
76
  end
@@ -0,0 +1,14 @@
1
+ class ZyNOSADSL < Oxidized::Model
2
+ using Refinements
3
+
4
+ # Used in Zyxel ADSL, such as AAM1212-51
5
+
6
+ prompt /^.*>\s?$/
7
+ comment ';; '
8
+
9
+ cmd 'config show all nopause'
10
+
11
+ cfg :telnet do
12
+ password /^Password:/i
13
+ end
14
+ end
@@ -30,7 +30,7 @@ class ZyNOSCLI < Oxidized::Model
30
30
  expect /[pP]assword:\s?$/
31
31
  send vars(:enable) + "\n"
32
32
  end
33
- expect /^.+[#]$/
33
+ expect /^.+\#$/
34
34
  end
35
35
  end
36
36
  pre_logout 'exit'
@@ -18,6 +18,8 @@ class ZyNOSGS < Oxidized::Model
18
18
  # Remove garbage vt100 control sequences
19
19
  # Backspace 0x07 char or escape char + control chars
20
20
  cfg.gsub! /[\b]|\e\[A|\e\[2K/, ''
21
+ # Remove empty lines
22
+ cfg.gsub! "\n\n", "\n"
21
23
  cfg
22
24
  end
23
25
 
@@ -0,0 +1,32 @@
1
+ class ZyNOSMGS < Oxidized::Model
2
+ using Refinements
3
+
4
+ PROMPT = /^(\w.*)>(.*)?$/
5
+ # Used in Zyxel MGS Series switches
6
+
7
+ prompt PROMPT
8
+ comment '! '
9
+
10
+ cmd 'show version' do |cfg|
11
+ clear_output cfg
12
+ end
13
+
14
+ cmd 'show running-config' do |cfg|
15
+ clear_output cfg
16
+ end
17
+
18
+ cfg :telnet do
19
+ username /^User\s?name(\(1-32 chars\))?:/i
20
+ password /^Password(\(1-32 chars\))?:/i
21
+ end
22
+
23
+ cfg :telnet, :ssh do
24
+ pre_logout 'exit'
25
+ end
26
+
27
+ private
28
+
29
+ def clear_output(output)
30
+ output.gsub PROMPT, ''
31
+ end
32
+ end
data/lib/oxidized/node.rb CHANGED
@@ -1,20 +1,23 @@
1
1
  module Oxidized
2
2
  require 'resolv'
3
- require 'ostruct'
4
3
  require_relative 'node/stats'
5
4
  class MethodNotFound < OxidizedError; end
6
5
  class ModelNotFound < OxidizedError; end
7
6
 
8
7
  class Node
9
- attr_reader :name, :ip, :model, :input, :output, :group, :auth, :prompt, :vars, :last, :repo
10
- attr_accessor :running, :user, :email, :msg, :from, :stats, :retry, :err_type, :err_reason
8
+ include SemanticLogger::Loggable
9
+
10
+ attr_reader :name, :ip, :model, :input, :output, :group, :auth, :prompt, :timeout, :vars, :last, :repo
11
+ attr_accessor :running, :user, :email, :msg, :from, :stats, :retry, :err_type, :err_reason, :nexted
11
12
  alias running? running
13
+ alias nexted? nexted
12
14
 
15
+ # opt is a hash with the node parameters given in the source (:name, :group, :ip...)
13
16
  def initialize(opt)
14
- Oxidized.logger.debug 'resolving DNS for %s...' % opt[:name]
17
+ logger.debug 'resolving DNS for %s...' % opt[:name]
15
18
  # remove the prefix if an IP Address is provided with one as IPAddr converts it to a network address.
16
19
  ip_addr, = opt[:ip].to_s.split("/")
17
- Oxidized.logger.debug 'IPADDR %s' % ip_addr.to_s
20
+ logger.debug 'IPADDR %s' % ip_addr.to_s
18
21
  @name = opt[:name]
19
22
  @ip = IPAddr.new(ip_addr).to_s rescue nil
20
23
  @ip ||= Resolv.new.getaddress(@name) if Oxidized.config.resolve_dns?
@@ -25,47 +28,52 @@ module Oxidized
25
28
  @output = resolve_output opt
26
29
  @auth = resolve_auth opt
27
30
  @prompt = resolve_prompt opt
28
- @vars = opt[:vars]
31
+ @timeout = resolve_timeout opt
32
+ @vars = opt[:vars] || {}
29
33
  @stats = Stats.new
30
34
  @retry = 0
31
35
  @repo = resolve_repo opt
32
36
  @err_type = nil
33
37
  @err_reason = nil
38
+ @nexted = false
34
39
 
35
40
  # model instance needs to access node instance
36
41
  @model.node = self
37
42
  end
38
43
 
39
44
  def run
40
- status, config = :fail, nil
41
- @input.each do |input|
42
- # don't try input if model is missing config block, we may need strong config to class_name map
43
- cfg_name = input.to_s.split('::').last.downcase
44
- next unless @model.cfg[cfg_name] && (not @model.cfg[cfg_name].empty?)
45
-
46
- @model.input = input = input.new
47
- if (config = run_input(input))
48
- Oxidized.logger.debug "lib/oxidized/node.rb: #{input.class.name} ran for #{name} successfully"
49
- status = :success
50
- break
45
+ status = :fail
46
+ config = Oxidized::Model::Outputs.new
47
+ input_sequence = @model.class.input_sequence(@input)
48
+
49
+ input_sequence.each do |sequence|
50
+ status = :fail
51
+ sequence_config = nil
52
+ sequence.each do |input|
53
+ @model.input = input = input.new
54
+ if (sequence_config = run_input(input))
55
+ logger.debug "#{input.class.name} ran for #{name} successfully"
56
+ status = :success
57
+ break
58
+ else
59
+ logger.debug "#{input.class.name} failed for #{name}"
60
+ status = :no_connection
61
+ end
62
+ end
63
+ if status == :success
64
+ config.merge! sequence_config
51
65
  else
52
- Oxidized.logger.debug "lib/oxidized/node.rb: #{input.class.name} failed for #{name}"
53
- status = :no_connection
66
+ config = nil
67
+ break
54
68
  end
55
69
  end
70
+
56
71
  @model.input = nil
57
72
  [status, config]
58
73
  end
59
74
 
60
75
  def run_input(input)
61
- rescue_fail = {}
62
- [input.class::RescueFail, input.class.superclass::RescueFail].each do |hash|
63
- hash.each do |level, errors|
64
- errors.each do |err|
65
- rescue_fail[err] = level
66
- end
67
- end
68
- end
76
+ rescue_fail = input.class.rescue_fail
69
77
  begin
70
78
  input.connect(self) && input.get
71
79
  rescue *rescue_fail.keys => err
@@ -75,24 +83,31 @@ module Oxidized
75
83
  level = rescue_fail[resc]
76
84
  resc = " (rescued #{resc})"
77
85
  end
78
- Oxidized.logger.send(level, '%s raised %s%s with msg "%s"' % [ip, err.class, resc, err.message])
86
+ logger.send(level, '%s raised %s%s with msg "%s"' % [ip, err.class, resc, err.message])
79
87
  @err_type = err.class.to_s
80
88
  @err_reason = err.message.to_s
81
89
  false
82
- rescue StandardError => err
90
+ rescue StandardError => e
91
+ # Send a message in debug mode in case we are not able to create a crashfile
92
+ logger.error "#{ip} raised #{e.class} with msg #{e.message}, creating crashfile"
93
+ unless Oxidized.config.crash.directory?
94
+ logger.error "Cannot create crashfile for exception", e
95
+ return false
96
+ end
97
+
83
98
  crashdir = Oxidized.config.crash.directory
84
99
  crashfile = Oxidized.config.crash.hostnames? ? name : ip.to_s
85
100
  FileUtils.mkdir_p(crashdir) unless File.directory?(crashdir)
86
101
 
87
102
  File.open File.join(crashdir, crashfile), 'w' do |fh|
88
103
  fh.puts Time.now.utc
89
- fh.puts err.message + ' [' + err.class.to_s + ']'
104
+ fh.puts e.message + ' [' + e.class.to_s + ']'
90
105
  fh.puts '-' * 50
91
- fh.puts err.backtrace
106
+ fh.puts e.backtrace
92
107
  end
93
- Oxidized.logger.error '%s raised %s with msg "%s", %s saved' % [ip, err.class, err.message, crashfile]
94
- @err_type = err.class.to_s
95
- @err_reason = err.message.to_s
108
+ logger.error '%s raised %s with msg "%s", %s saved' % [ip, e.class, e.message, crashfile]
109
+ @err_type = e.class.to_s
110
+ @err_reason = e.message.to_s
96
111
  false
97
112
  end
98
113
  end
@@ -120,14 +135,10 @@ module Oxidized
120
135
  h
121
136
  end
122
137
 
138
+ JobStruct = Struct.new(:start, :end, :status, :time)
123
139
  def last=(job)
124
140
  if job
125
- ostruct = OpenStruct.new
126
- ostruct.start = job.start
127
- ostruct.end = job.end
128
- ostruct.status = job.status
129
- ostruct.time = job.time
130
- @last = ostruct
141
+ @last = JobStruct.new(job.start, job.end, job.status, job.time)
131
142
  else
132
143
  @last = nil
133
144
  end
@@ -148,6 +159,10 @@ module Oxidized
148
159
  opt[:prompt] || @model.prompt || Oxidized.config.prompt
149
160
  end
150
161
 
162
+ def resolve_timeout(opt)
163
+ resolve_key :timeout, opt, Oxidized.config.timeout
164
+ end
165
+
151
166
  def resolve_auth(opt)
152
167
  # Resolve configured username/password
153
168
  {
@@ -158,8 +173,11 @@ module Oxidized
158
173
 
159
174
  def resolve_input(opt)
160
175
  inputs = resolve_key :input, opt, Oxidized.config.input.default
161
- inputs.split(/\s*,\s*/).map do |input|
162
- Oxidized.mgr.add_input(input) || raise(MethodNotFound, "#{input} not found for node #{ip}") unless Oxidized.mgr.input[input]
176
+ inputs.split(',').map do |input|
177
+ input.strip!
178
+ unless Oxidized.mgr.input[input]
179
+ Oxidized.mgr.add_input(input) || raise(MethodNotFound, "#{input} not found for node #{ip}")
180
+ end
163
181
 
164
182
  Oxidized.mgr.input[input]
165
183
  end
@@ -167,7 +185,10 @@ module Oxidized
167
185
 
168
186
  def resolve_output(opt)
169
187
  output = resolve_key :output, opt, Oxidized.config.output.default
170
- Oxidized.mgr.add_output(output) || raise(MethodNotFound, "#{output} not found for node #{ip}") unless Oxidized.mgr.output[output]
188
+ unless Oxidized.mgr.output[output]
189
+ Oxidized.mgr.add_output(output) || raise(MethodNotFound,
190
+ "#{output} not found for node #{ip}")
191
+ end
171
192
 
172
193
  Oxidized.mgr.output[output]
173
194
  end
@@ -175,7 +196,7 @@ module Oxidized
175
196
  def resolve_model(opt)
176
197
  model = resolve_key :model, opt
177
198
  unless Oxidized.mgr.model[model]
178
- Oxidized.logger.debug "lib/oxidized/node.rb: Loading model #{model.inspect}"
199
+ logger.debug "Loading model #{model.inspect}"
179
200
  Oxidized.mgr.add_model(model) || raise(ModelNotFound, "#{model} not found for node #{ip}")
180
201
  end
181
202
  Oxidized.mgr.model[model].new
@@ -198,33 +219,47 @@ module Oxidized
198
219
  end
199
220
 
200
221
  def resolve_key(key, opt, global = nil)
201
- # resolve key, first get global, then get group then get node config
222
+ # resolve key: the priority is as follows:
223
+ # node -> group specific model -> group -> model -> global passed -> global
224
+ # where node has the highest priority (= if defined, overwrites other values)
202
225
  key_sym = key.to_sym
203
226
  key_str = key.to_s
204
- value = global
205
- Oxidized.logger.debug "node.rb: resolving node key '#{key}', with passed global value of '#{value}' and node value '#{opt[key_sym]}'"
227
+ model_name = @model.class.name.to_s.downcase
228
+ logger.debug "resolving node key '#{key}', with passed global value of '#{global}' " \
229
+ "and node value '#{opt[key_sym]}'"
206
230
 
207
- # global
208
- if (not value) && Oxidized.config.has_key?(key_str)
209
- value = Oxidized.config[key_str]
210
- Oxidized.logger.debug "node.rb: setting node key '#{key}' to value '#{value}' from global"
211
- end
231
+ # Node
232
+ if opt[key_sym]
233
+ value = opt[key_sym]
234
+ logger.debug "setting node key '#{key}' to value '#{value}' from node"
212
235
 
213
- # group
214
- if Oxidized.config.groups.has_key?(@group) && Oxidized.config.groups[@group].has_key?(key_str)
236
+ # Group specific model
237
+ elsif Oxidized.config.groups.has_key?(@group) &&
238
+ Oxidized.config.groups[@group].models.has_key?(model_name) &&
239
+ Oxidized.config.groups[@group].models[model_name].has_key?(key_str)
240
+ value = Oxidized.config.groups[@group].models[model_name][key_str]
241
+ logger.debug "setting node key '#{key}' to value '#{value}' from model in group"
242
+
243
+ # Group
244
+ elsif Oxidized.config.groups.has_key?(@group) && Oxidized.config.groups[@group].has_key?(key_str)
215
245
  value = Oxidized.config.groups[@group][key_str]
216
- Oxidized.logger.debug "node.rb: setting node key '#{key}' to value '#{value}' from group"
217
- end
246
+ logger.debug "setting node key '#{key}' to value '#{value}' from group"
218
247
 
219
- # model
220
- if Oxidized.config.models.has_key?(@model.class.name.to_s.downcase) && Oxidized.config.models[@model.class.name.to_s.downcase].has_key?(key_str)
221
- value = Oxidized.config.models[@model.class.name.to_s.downcase][key_str]
222
- Oxidized.logger.debug "node.rb: setting node key '#{key}' to value '#{value}' from model"
223
- end
248
+ # Model
249
+ elsif Oxidized.config.models.has_key?(model_name) && Oxidized.config.models[model_name].has_key?(key_str)
250
+ value = Oxidized.config.models[model_name][key_str]
251
+ logger.debug "setting node key '#{key}' to value '#{value}' from model"
224
252
 
225
- # node
226
- value = opt[key_sym] || value
227
- Oxidized.logger.debug "node.rb: returning node key '#{key}' with value '#{value}'"
253
+ # Global passed
254
+ elsif global
255
+ value = global
256
+ logger.debug "setting node key '#{key}' to value '#{value}' from passed global value"
257
+
258
+ # Global
259
+ elsif Oxidized.config.has_key?(key_str)
260
+ value = Oxidized.config[key_str]
261
+ logger.debug "setting node key '#{key}' to value '#{value}' from global"
262
+ end
228
263
  value
229
264
  end
230
265
 
@@ -1,10 +1,12 @@
1
1
  module Oxidized
2
2
  require 'ipaddr'
3
3
  require 'oxidized/node'
4
- class Oxidized::NotSupported < OxidizedError; end
5
- class Oxidized::NodeNotFound < OxidizedError; end
4
+ class NotSupported < OxidizedError; end
5
+ class NodeNotFound < OxidizedError; end
6
6
 
7
7
  class Nodes < Array
8
+ include SemanticLogger::Loggable
9
+
8
10
  attr_accessor :source, :jobs
9
11
  alias put unshift
10
12
  def load(node_want = nil)
@@ -12,7 +14,7 @@ module Oxidized
12
14
  new = []
13
15
  @source = Oxidized.config.source.default
14
16
  Oxidized.mgr.add_source(@source) || raise(MethodNotFound, "cannot load node source '#{@source}', not found")
15
- Oxidized.logger.info "lib/oxidized/nodes.rb: Loading nodes"
17
+ logger.info "Loading nodes"
16
18
  nodes = Oxidized.mgr.source[@source].new.load node_want
17
19
  nodes.each do |node|
18
20
  # we want to load specific node(s), not all of them
@@ -21,22 +23,25 @@ module Oxidized
21
23
  begin
22
24
  node_obj = Node.new node
23
25
  new.push node_obj
24
- rescue ModelNotFound => err
25
- Oxidized.logger.error "node %s raised %s with message '%s'" % [node, err.class, err.message]
26
- rescue Resolv::ResolvError => err
27
- Oxidized.logger.error "node %s is not resolvable, raised %s with message '%s'" % [node, err.class, err.message]
26
+ rescue ModelNotFound => e
27
+ logger.error "node %s raised %s with message '%s'" % [node, e.class, e.message]
28
+ rescue Resolv::ResolvError => e
29
+ logger.error "node %s is not resolvable, raised %s with message '%s'" % [node, e.class, e.message]
28
30
  end
29
31
  end
30
32
  size.zero? ? replace(new) : update_nodes(new)
31
- Oxidized.logger.info "lib/oxidized/nodes.rb: Loaded #{size} nodes"
33
+ Output.clean_obsolete_nodes(self) if node_want.nil?
34
+ logger.info "Loaded #{size} nodes"
32
35
  end
33
36
  end
34
37
 
35
38
  def node_want?(node_want, node)
36
39
  return true unless node_want
37
40
 
41
+ # rubocop:disable Style/RedundantParentheses
38
42
  node_want_ip = (IPAddr.new(node_want) rescue false)
39
43
  name_is_ip = (IPAddr.new(node[:name]) rescue false)
44
+ # rubocop:enable Style/RedundantParentheses
40
45
  # rubocop:todo Lint/DuplicateBranch
41
46
  if name_is_ip && (node_want_ip == node[:name])
42
47
  true
@@ -61,6 +66,9 @@ module Oxidized
61
66
  end
62
67
  end
63
68
 
69
+ # Returns the configuration of group/node_name
70
+ #
71
+ # #fetch is called by oxidzed-web
64
72
  def fetch(node_name, group)
65
73
  yield_node_output(node_name) do |node, output|
66
74
  output.fetch node, group
@@ -69,8 +77,9 @@ module Oxidized
69
77
 
70
78
  # @param node [String] name of the node moved into the head of array
71
79
  def next(node, opt = {})
72
- return unless waiting.find_node_index(node)
80
+ return if running.find_index(node)
73
81
 
82
+ logger.info "Add node #{node} to running jobs"
74
83
  with_lock do
75
84
  n = del node
76
85
  n.user = opt['user']
@@ -79,8 +88,10 @@ module Oxidized
79
88
  n.from = opt['from']
80
89
  # set last job to nil so that the node is picked for immediate update
81
90
  n.last = nil
91
+ # set nexted to true so that the node will not be skipped with interval 0
92
+ n.nexted = true
82
93
  put n
83
- jobs.want += 1 if Oxidized.config.next_adds_job?
94
+ jobs.increment if Oxidized.config.next_adds_job?
84
95
  end
85
96
  end
86
97
  alias top next
@@ -95,9 +106,12 @@ module Oxidized
95
106
  # @param node node whose index number in Nodes to find
96
107
  # @return [Fixnum] index number of node in Nodes
97
108
  def find_node_index(node)
98
- find_index(node) || raise(Oxidized::NodeNotFound, "unable to find '#{node}'")
109
+ find_index(node) || raise(NodeNotFound, "unable to find '#{node}'")
99
110
  end
100
111
 
112
+ # Returns all stored versions of group/node_name
113
+ #
114
+ # Called by oxidized-web
101
115
  def version(node_name, group)
102
116
  yield_node_output(node_name) do |node, output|
103
117
  output.version node, group
@@ -116,6 +130,10 @@ module Oxidized
116
130
  end
117
131
  end
118
132
 
133
+ def find_index(node)
134
+ index { |e| [e.name, e.ip].include? node }
135
+ end
136
+
119
137
  private
120
138
 
121
139
  def initialize(opts = {})
@@ -129,12 +147,8 @@ module Oxidized
129
147
  end
130
148
  end
131
149
 
132
- def with_lock(&block)
133
- @mutex.synchronize(&block)
134
- end
135
-
136
- def find_index(node)
137
- index { |e| [e.name, e.ip].include? node }
150
+ def with_lock(...)
151
+ @mutex.synchronize(...)
138
152
  end
139
153
 
140
154
  # @param node node which is removed from nodes list
@@ -160,13 +174,18 @@ module Oxidized
160
174
  # @param [Array] nodes Array of nodes used to replace+update old
161
175
  def update_nodes(nodes)
162
176
  old = dup
177
+ # load the Array "nodes" in self (the class Nodes inherits Array)
163
178
  replace(nodes)
164
179
  each do |node|
165
180
  if (i = old.find_node_index(node.name))
166
181
  node.stats = old[i].stats
167
182
  node.last = old[i].last
168
183
  end
169
- rescue Oxidized::NodeNotFound
184
+ rescue NodeNotFound
185
+ # Do nothing:
186
+ # when a node is not found, we have nothing to do:
187
+ # it has already been loaded by replace(nodes) and there are no
188
+ # stats to copy
170
189
  end
171
190
  sort_by! { |x| x.last.nil? ? Time.new(0) : x.last.end }
172
191
  end
@@ -174,8 +193,10 @@ module Oxidized
174
193
  def yield_node_output(node_name)
175
194
  with_lock do
176
195
  node = find { |n| n.name == node_name }
196
+ raise(NodeNotFound, "unable to find '#{node_name}'") if node.nil?
197
+
177
198
  output = node.output.new
178
- raise Oxidized::NotSupported unless output.respond_to? :fetch
199
+ raise NotSupported unless output.respond_to? :fetch
179
200
 
180
201
  yield node, output
181
202
  end