oxidized 0.20.0 → 0.28.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (222) hide show
  1. checksums.yaml +5 -5
  2. data/.codeclimate.yml +4 -0
  3. data/.github/PULL_REQUEST_TEMPLATE.md +12 -0
  4. data/.github/no-response.yml +13 -0
  5. data/.github/workflows/publishdocker.yml +13 -0
  6. data/.gitignore +4 -0
  7. data/.rubocop.yml +73 -0
  8. data/.rubocop_todo.yml +120 -0
  9. data/.travis.yml +6 -1
  10. data/CHANGELOG.md +693 -243
  11. data/Dockerfile +27 -19
  12. data/LICENSE +201 -0
  13. data/README.md +234 -913
  14. data/Rakefile +48 -7
  15. data/TODO.md +29 -23
  16. data/bin/console +1 -1
  17. data/bin/oxidized +6 -5
  18. data/docs/Configuration.md +313 -0
  19. data/docs/Creating-Models.md +140 -0
  20. data/docs/Hooks.md +274 -0
  21. data/docs/Model-Notes/AireOS.md +11 -0
  22. data/docs/Model-Notes/ArbOS.md +11 -0
  23. data/docs/Model-Notes/Comware.md +13 -0
  24. data/docs/Model-Notes/Cumulus.md +40 -0
  25. data/docs/Model-Notes/EOS.md +12 -0
  26. data/docs/Model-Notes/IOS.md +29 -0
  27. data/docs/Model-Notes/JunOS.md +33 -0
  28. data/docs/Model-Notes/LinuxGeneric.md +24 -0
  29. data/docs/Model-Notes/Netgear.md +87 -0
  30. data/docs/Model-Notes/Nokia.md +9 -0
  31. data/docs/Model-Notes/README.md +24 -0
  32. data/docs/Model-Notes/SmartAX-Huawei.md +35 -0
  33. data/docs/Model-Notes/VRP-Huawei.md +34 -0
  34. data/docs/Model-Notes/Viptela.md +12 -0
  35. data/docs/Model-Notes/XGS4600-Zyxel.md +36 -0
  36. data/docs/Outputs.md +190 -0
  37. data/docs/Ruby-API.md +199 -0
  38. data/docs/Sources.md +171 -0
  39. data/docs/Supported-OS-Types.md +227 -0
  40. data/docs/Troubleshooting.md +66 -0
  41. data/extra/nagios_check_failing_nodes.rb +9 -2
  42. data/extra/oxidized-report-git-commits +21 -40
  43. data/extra/oxidized-ubuntu.haproxy +45 -0
  44. data/extra/oxidized.logrotate +7 -0
  45. data/extra/oxidized.service +13 -0
  46. data/extra/rest_client.rb +7 -10
  47. data/extra/syslog.rb +47 -42
  48. data/lib/oxidized/cli.rb +41 -31
  49. data/lib/oxidized/config/vars.rb +9 -14
  50. data/lib/oxidized/config.rb +20 -13
  51. data/lib/oxidized/core.rb +8 -10
  52. data/lib/oxidized/hook/awssns.rb +6 -7
  53. data/lib/oxidized/hook/ciscosparkdiff.rb +43 -0
  54. data/lib/oxidized/hook/exec.rb +19 -24
  55. data/lib/oxidized/hook/githubrepo.rb +17 -17
  56. data/lib/oxidized/hook/noophook.rb +1 -1
  57. data/lib/oxidized/hook/slackdiff.rb +32 -19
  58. data/lib/oxidized/hook/xmppdiff.rb +59 -0
  59. data/lib/oxidized/hook.rb +63 -64
  60. data/lib/oxidized/input/cli.rb +22 -12
  61. data/lib/oxidized/input/exec.rb +28 -0
  62. data/lib/oxidized/input/ftp.rb +16 -15
  63. data/lib/oxidized/input/http.rb +72 -0
  64. data/lib/oxidized/input/input.rb +6 -6
  65. data/lib/oxidized/input/ssh.rb +64 -56
  66. data/lib/oxidized/input/telnet.rb +59 -102
  67. data/lib/oxidized/input/tftp.rb +9 -10
  68. data/lib/oxidized/jobs.rb +9 -10
  69. data/lib/oxidized/manager.rb +42 -44
  70. data/lib/oxidized/model/acos.rb +19 -20
  71. data/lib/oxidized/model/acsw.rb +62 -0
  72. data/lib/oxidized/model/adtran.rb +26 -0
  73. data/lib/oxidized/model/aen.rb +19 -0
  74. data/lib/oxidized/model/aireos.rb +9 -10
  75. data/lib/oxidized/model/airfiber.rb +22 -0
  76. data/lib/oxidized/model/alteonos.rb +58 -0
  77. data/lib/oxidized/model/alvarion.rb +0 -4
  78. data/lib/oxidized/model/aos.rb +11 -5
  79. data/lib/oxidized/model/aos7.rb +6 -7
  80. data/lib/oxidized/model/aosw.rb +30 -27
  81. data/lib/oxidized/model/apc_aos.rb +2 -5
  82. data/lib/oxidized/model/arbos.rb +26 -0
  83. data/lib/oxidized/model/aricentiss.rb +49 -0
  84. data/lib/oxidized/model/asa.rb +61 -22
  85. data/lib/oxidized/model/asyncos.rb +46 -0
  86. data/lib/oxidized/model/audiocodes.rb +28 -0
  87. data/lib/oxidized/model/audiocodesmp.rb +28 -0
  88. data/lib/oxidized/model/awplus.rb +84 -0
  89. data/lib/oxidized/model/axos.rb +16 -0
  90. data/lib/oxidized/model/boss.rb +77 -0
  91. data/lib/oxidized/model/br6910.rb +42 -45
  92. data/lib/oxidized/model/c4cmts.rb +6 -10
  93. data/lib/oxidized/model/cambium.rb +23 -0
  94. data/lib/oxidized/model/casa.rb +1 -1
  95. data/lib/oxidized/model/catos.rb +1 -3
  96. data/lib/oxidized/model/cisconga.rb +1 -3
  97. data/lib/oxidized/model/ciscosma.rb +42 -0
  98. data/lib/oxidized/model/ciscosmb.rb +30 -10
  99. data/lib/oxidized/model/ciscovpn3k.rb +11 -0
  100. data/lib/oxidized/model/cnos.rb +33 -0
  101. data/lib/oxidized/model/comnetms.rb +43 -0
  102. data/lib/oxidized/model/comtrol.rb +41 -0
  103. data/lib/oxidized/model/comware.rb +28 -16
  104. data/lib/oxidized/model/coriant8600.rb +3 -5
  105. data/lib/oxidized/model/coriantgroove.rb +26 -0
  106. data/lib/oxidized/model/corianttmos.rb +1 -3
  107. data/lib/oxidized/model/cumulus.rb +60 -49
  108. data/lib/oxidized/model/datacom.rb +1 -4
  109. data/lib/oxidized/model/dcnos.rb +46 -0
  110. data/lib/oxidized/model/dellx.rb +76 -0
  111. data/lib/oxidized/model/dlink.rb +5 -4
  112. data/lib/oxidized/model/dnos.rb +11 -5
  113. data/lib/oxidized/model/eciapollo.rb +34 -0
  114. data/lib/oxidized/model/edgecos.rb +49 -0
  115. data/lib/oxidized/model/edgeos.rb +12 -5
  116. data/lib/oxidized/model/edgeswitch.rb +2 -4
  117. data/lib/oxidized/model/enterasys.rb +28 -0
  118. data/lib/oxidized/model/eos.rb +8 -8
  119. data/lib/oxidized/model/fabricos.rb +4 -6
  120. data/lib/oxidized/model/fastiron.rb +66 -0
  121. data/lib/oxidized/model/fiberdriver.rb +2 -2
  122. data/lib/oxidized/model/firebrick.rb +31 -0
  123. data/lib/oxidized/model/firelinuxos.rb +41 -0
  124. data/lib/oxidized/model/firewareos.rb +3 -6
  125. data/lib/oxidized/model/fortios.rb +31 -19
  126. data/lib/oxidized/model/ftos.rb +8 -5
  127. data/lib/oxidized/model/fujitsupy.rb +5 -7
  128. data/lib/oxidized/model/gaiaos.rb +7 -11
  129. data/lib/oxidized/model/gcombnps.rb +84 -0
  130. data/lib/oxidized/model/grandstream.rb +9 -0
  131. data/lib/oxidized/model/hatteras.rb +9 -6
  132. data/lib/oxidized/model/hirschmann.rb +39 -0
  133. data/lib/oxidized/model/hpebladesystem.rb +20 -18
  134. data/lib/oxidized/model/hpemsa.rb +10 -0
  135. data/lib/oxidized/model/hpmsm.rb +84 -0
  136. data/lib/oxidized/model/ibos.rb +55 -0
  137. data/lib/oxidized/model/icotera.rb +27 -0
  138. data/lib/oxidized/model/ios.rb +63 -70
  139. data/lib/oxidized/model/iosxe.rb +5 -0
  140. data/lib/oxidized/model/iosxr.rb +2 -3
  141. data/lib/oxidized/model/ipos.rb +10 -6
  142. data/lib/oxidized/model/ironware.rb +20 -19
  143. data/lib/oxidized/model/isam.rb +5 -6
  144. data/lib/oxidized/model/junos.rb +9 -11
  145. data/lib/oxidized/model/linuxgeneric.rb +74 -0
  146. data/lib/oxidized/model/masteros.rb +3 -6
  147. data/lib/oxidized/model/mlnxos.rb +9 -10
  148. data/lib/oxidized/model/model.rb +72 -46
  149. data/lib/oxidized/model/mtrlrfs.rb +1 -4
  150. data/lib/oxidized/model/ndms.rb +23 -0
  151. data/lib/oxidized/model/netgear.rb +35 -15
  152. data/lib/oxidized/model/netonix.rb +2 -2
  153. data/lib/oxidized/model/netscaler.rb +6 -3
  154. data/lib/oxidized/model/nos.rb +5 -7
  155. data/lib/oxidized/model/nsxconfig.rb +22 -0
  156. data/lib/oxidized/model/nsxfirewall.rb +22 -0
  157. data/lib/oxidized/model/nxos.rb +13 -3
  158. data/lib/oxidized/model/oneos.rb +15 -9
  159. data/lib/oxidized/model/openbsd.rb +63 -0
  160. data/lib/oxidized/model/opengear.rb +3 -5
  161. data/lib/oxidized/model/openwrt.rb +78 -0
  162. data/lib/oxidized/model/opnsense.rb +19 -0
  163. data/lib/oxidized/model/os10.rb +46 -0
  164. data/lib/oxidized/model/outputs.rb +5 -7
  165. data/lib/oxidized/model/panos.rb +11 -12
  166. data/lib/oxidized/model/pfsense.rb +11 -6
  167. data/lib/oxidized/model/planet.rb +14 -17
  168. data/lib/oxidized/model/powerconnect.rb +24 -19
  169. data/lib/oxidized/model/procurve.rb +43 -11
  170. data/lib/oxidized/model/purityos.rb +12 -0
  171. data/lib/oxidized/model/qtech.rb +41 -0
  172. data/lib/oxidized/model/quantaos.rb +4 -6
  173. data/lib/oxidized/model/raisecom.rb +19 -0
  174. data/lib/oxidized/model/routeros.rb +26 -8
  175. data/lib/oxidized/model/saos.rb +1 -2
  176. data/lib/oxidized/model/screenos.rb +8 -11
  177. data/lib/oxidized/model/sgos.rb +45 -0
  178. data/lib/oxidized/model/siklu.rb +1 -3
  179. data/lib/oxidized/model/slxos.rb +59 -0
  180. data/lib/oxidized/model/smartax.rb +25 -0
  181. data/lib/oxidized/model/sonicos.rb +51 -0
  182. data/lib/oxidized/model/speedtouch.rb +34 -0
  183. data/lib/oxidized/model/sros.rb +96 -0
  184. data/lib/oxidized/model/stoneos.rb +32 -0
  185. data/lib/oxidized/model/supermicro.rb +6 -41
  186. data/lib/oxidized/model/tdre.rb +30 -0
  187. data/lib/oxidized/model/telco.rb +24 -0
  188. data/lib/oxidized/model/timos.rb +6 -114
  189. data/lib/oxidized/model/tmos.rb +6 -3
  190. data/lib/oxidized/model/tplink.rb +11 -11
  191. data/lib/oxidized/model/trango.rb +21 -42
  192. data/lib/oxidized/model/ucs.rb +30 -0
  193. data/lib/oxidized/model/viptela.rb +29 -0
  194. data/lib/oxidized/model/voltaire.rb +9 -12
  195. data/lib/oxidized/model/voss.rb +17 -6
  196. data/lib/oxidized/model/vrp.rb +11 -6
  197. data/lib/oxidized/model/vyatta.rb +8 -6
  198. data/lib/oxidized/model/weos.rb +20 -0
  199. data/lib/oxidized/model/xos.rb +20 -8
  200. data/lib/oxidized/model/zhoneolt.rb +2 -2
  201. data/lib/oxidized/model/zynos.rb +1 -3
  202. data/lib/oxidized/model/zynoscli.rb +36 -0
  203. data/lib/oxidized/model/zynosgs.rb +38 -0
  204. data/lib/oxidized/node/stats.rb +33 -8
  205. data/lib/oxidized/node.rb +86 -95
  206. data/lib/oxidized/nodes.rb +48 -44
  207. data/lib/oxidized/output/file.rb +32 -37
  208. data/lib/oxidized/output/git.rb +138 -153
  209. data/lib/oxidized/output/gitcrypt.rb +228 -242
  210. data/lib/oxidized/output/http.rb +35 -34
  211. data/lib/oxidized/output/output.rb +2 -3
  212. data/lib/oxidized/source/csv.rb +50 -44
  213. data/lib/oxidized/source/http.rb +58 -58
  214. data/lib/oxidized/source/source.rb +9 -10
  215. data/lib/oxidized/source/sql.rb +47 -45
  216. data/lib/oxidized/string.rb +18 -14
  217. data/lib/oxidized/version.rb +17 -1
  218. data/lib/oxidized/worker.rb +72 -33
  219. data/oxidized.gemspec +20 -19
  220. metadata +180 -36
  221. data/.ruby-version +0 -1
  222. data/Gemfile.lock +0 -44
data/extra/syslog.rb CHANGED
@@ -27,45 +27,46 @@ require 'resolv'
27
27
  require_relative 'rest_client'
28
28
 
29
29
  module Oxidized
30
-
31
30
  require 'asetus'
32
31
  class Config
33
- Root = File.join ENV['HOME'], '.config', 'oxidized'
32
+ Root = File.join ENV['HOME'], '.config', 'oxidized'
34
33
  end
35
34
 
36
- CFGS = Asetus.new :name=>'oxidized', :load=>false, :key_to_s=>true
35
+ CFGS = Asetus.new name: 'oxidized', load: false, key_to_s: true
37
36
  CFGS.default.syslogd.port = 514
38
37
  CFGS.default.syslogd.file = 'messages'
39
38
  CFGS.default.syslogd.resolve = true
39
+ CFGS.default.syslogd.dns_map = {
40
+ '(.*)\.strip\.this\.domain\.com' => '\\1',
41
+ '(.*)\.also\.this\.net' => '\\1'
42
+ }
40
43
 
41
44
  begin
42
45
  CFGS.load
43
- rescue => error
46
+ rescue StandardError => error
44
47
  raise InvalidConfig, "Error loading config: #{error.message}"
45
48
  ensure
46
- CFG = CFGS.cfg # convenienence, instead of Config.cfg.password, CFG.password
49
+ CFG = CFGS.cfg # convenienence, instead of Config.cfg.password, CFG.password
47
50
  end
48
51
 
49
52
  class SyslogMonitor
50
- NAME_MAP = {
51
- /(.*)\.ip\.tdc\.net/ => '\1',
52
- /(.*)\.ip\.fi/ => '\1',
53
- }
54
53
  MSG = {
55
- :ios => /%SYS-(SW[0-9]+-)?5-CONFIG_I:/,
56
- :junos => 'UI_COMMIT:',
57
- :eos => /%SYS-5-CONFIG_I:/,
58
- :nxos => /%VSHD-5-VSHD_SYSLOG_CONFIG_I:/,
59
- }
54
+ ios: /%SYS-(SW[0-9]+-)?5-CONFIG_I:/,
55
+ junos: 'UI_COMMIT:',
56
+ eos: /%SYS-5-CONFIG_I:/,
57
+ nxos: /%VSHD-5-VSHD_SYSLOG_CONFIG_I:/,
58
+ aruba: 'Notice-Type=\'Running'
59
+ }.freeze
60
60
 
61
61
  class << self
62
- def udp port=Oxidized::CFG.syslogd.port, listen=0
62
+ def udp(port = Oxidized::CFG.syslogd.port, listen = 0)
63
63
  io = UDPSocket.new
64
64
  io.bind listen, port
65
65
  new io, :udp
66
66
  end
67
- def file syslog_file=Oxidized::CFG.syslogd.file
68
- io = open syslog_file, 'r'
67
+
68
+ def file(syslog_file = Oxidized::CFG.syslogd.file)
69
+ io = File.open syslog_file, 'r'
69
70
  io.seek 0, IO::SEEK_END
70
71
  new io, :file
71
72
  end
@@ -73,43 +74,47 @@ module Oxidized
73
74
 
74
75
  private
75
76
 
76
- def initialize io, mode=:udp
77
+ def initialize(io, mode = :udp)
77
78
  @mode = mode
78
79
  run io
79
80
  end
80
81
 
81
- def rest opt
82
+ def rest(opt)
82
83
  Oxidized::RestClient.next opt
83
84
  end
84
85
 
85
- def ios ip, log, i
86
+ def ios(log, index, **opts)
86
87
  # TODO: we need to fetch 'ip/name' in mode == :file here
87
- user = log[i+5]
88
- from = log[-1][1..-2]
89
- rest( :user => user, :from => from, :model => 'ios', :ip => ip,
90
- :name => getname(ip) )
88
+ opts[:user] = log[index + 5]
89
+ opts[:from] = log[-1][1..-2]
90
+ opts
91
91
  end
92
+ alias nxos ios
93
+ alias eos ios
92
94
 
93
- def jnpr ip, log, i
95
+ def junos(log, index, **opts)
94
96
  # TODO: we need to fetch 'ip/name' in mode == :file here
95
- user = log[i+2][1..-2]
96
- msg = log[(i+6)..-1].join(' ')[10..-2]
97
- msg = nil if msg == 'none'
98
- rest( :user => user, :msg => msg, :model => 'jnpr', :ip => ip,
99
- :name => getname(ip) )
97
+ opts[:user] = log[index + 2][1..-2]
98
+ opts[:msg] = log[(index + 6)..-1].join(' ')[10..-2]
99
+ opts.delete(:msg) if opts[:msg] == 'none'
100
+ opts
101
+ end
102
+
103
+ def aruba(log, index, **opts)
104
+ opts.merge user: log[index + 2].split('=')[4].split(',')[0][1..-2]
100
105
  end
101
106
 
102
- def handle_log log, ip
107
+ def handle_log(log, ipaddr)
103
108
  log = log.to_s.split ' '
104
- if i = log.find_index { |e| e.match( MSG[:ios] ) }
105
- ios ip, log, i
106
- elsif i = log.index(MSG[:junos])
107
- jnpr ip, log, i
109
+ index, vendor = MSG.find do |key, value|
110
+ index = log.find_index { |e| e.match value }
111
+ break index, key if index
108
112
  end
113
+ rest send(vendor, log, index, ip: ipaddr, name: getname(ipaddr), model: vendor.to_s) if index
109
114
  end
110
115
 
111
- def run io
112
- while true
116
+ def run(io)
117
+ loop do
113
118
  log = select [io]
114
119
  log, ip = log.first.first, nil
115
120
  if @mode == :udp
@@ -127,12 +132,12 @@ module Oxidized
127
132
  end
128
133
  end
129
134
 
130
- def getname ip
135
+ def getname(ipaddr)
131
136
  if Oxidized::CFG.syslogd.resolve == false
132
- ip
137
+ ipaddr
133
138
  else
134
- name = (Resolv.getname ip.to_s rescue ip)
135
- NAME_MAP.each { |re, sub| name.sub! re, sub }
139
+ name = (Resolv.getname ipaddr.to_s rescue ipaddr)
140
+ Oxidized::CFG.syslogd.dns_map.each { |re, sub| name.sub! Regexp.new(re.to_s), sub }
136
141
  name
137
142
  end
138
143
  end
@@ -140,4 +145,4 @@ module Oxidized
140
145
  end
141
146
 
142
147
  Oxidized::SyslogMonitor.udp
143
- #Oxidized::SyslogMonitor.file '/var/log/poop'
148
+ # Oxidized::SyslogMonitor.file '/var/log/poop'
data/lib/oxidized/cli.rb CHANGED
@@ -2,15 +2,16 @@ module Oxidized
2
2
  class CLI
3
3
  require 'slop'
4
4
  require 'oxidized'
5
+ require 'English'
5
6
 
6
7
  def run
7
8
  check_pid
8
9
  Process.daemon if @opts[:daemonize]
9
10
  write_pid
10
11
  begin
11
- Oxidized.logger.info "Oxidized starting, running as pid #{$$}"
12
+ Oxidized.logger.info "Oxidized starting, running as pid #{$PROCESS_ID}"
12
13
  Oxidized.new
13
- rescue => error
14
+ rescue StandardError => error
14
15
  crash error
15
16
  raise
16
17
  end
@@ -27,9 +28,9 @@ module Oxidized
27
28
  @pidfile = File.expand_path(Oxidized.config.pid)
28
29
  end
29
30
 
30
- def crash error
31
+ def crash(error)
31
32
  Oxidized.logger.fatal "Oxidized crashed, crashfile written in #{Config::Crash}"
32
- open Config::Crash, 'w' do |file|
33
+ File.open Config::Crash, 'w' do |file|
33
34
  file.puts '-' * 50
34
35
  file.puts Time.now.utc
35
36
  file.puts error.message + ' [' + error.class.to_s + ']'
@@ -40,53 +41,62 @@ module Oxidized
40
41
  end
41
42
 
42
43
  def parse_opts
43
- opts = Slop.new(:help=>true) do
44
- on 'd', 'debug', 'turn on debugging'
45
- on 'daemonize', 'Daemonize/fork the process'
46
- on 'v', 'version', 'show version' do
47
- puts Oxidized::VERSION
44
+ opts = Slop.parse do |opt|
45
+ opt.on '-d', '--debug', 'turn on debugging'
46
+ opt.on '--daemonize', 'Daemonize/fork the process'
47
+ opt.on '-h', '--help', 'show usage' do
48
+ puts opt
49
+ exit
50
+ end
51
+ opt.on '--show-exhaustive-config', 'output entire configuration, including defaults' do
52
+ asetus = Config.load
53
+ puts asetus.to_yaml asetus.cfg
54
+ Kernel.exit
55
+ end
56
+ opt.on '-v', '--version', 'show version' do
57
+ puts Oxidized::VERSION_FULL
48
58
  Kernel.exit
49
59
  end
50
60
  end
51
- [opts.parse!, opts]
61
+ [opts.arguments, opts]
52
62
  end
53
63
 
54
- def pidfile
55
- @pidfile
56
- end
64
+ attr_reader :pidfile
57
65
 
58
66
  def pidfile?
59
67
  !!pidfile
60
68
  end
61
69
 
62
70
  def write_pid
63
- if pidfile?
64
- begin
65
- File.open(pidfile, ::File::CREAT | ::File::EXCL | ::File::WRONLY){|f| f.write("#{Process.pid}") }
66
- at_exit { File.delete(pidfile) if File.exists?(pidfile) }
67
- rescue Errno::EEXIST
68
- check_pid
69
- retry
70
- end
71
+ return unless pidfile?
72
+
73
+ begin
74
+ File.open(pidfile, ::File::CREAT | ::File::EXCL | ::File::WRONLY) { |f| f.write(Process.pid.to_s) }
75
+ at_exit { File.delete(pidfile) if File.exist?(pidfile) }
76
+ rescue Errno::EEXIST
77
+ check_pid
78
+ retry
71
79
  end
72
80
  end
73
81
 
74
82
  def check_pid
75
- if pidfile?
76
- case pid_status(pidfile)
77
- when :running, :not_owned
78
- puts "A server is already running. Check #{pidfile}"
79
- exit(1)
80
- when :dead
81
- File.delete(pidfile)
82
- end
83
+ return unless pidfile?
84
+
85
+ case pid_status(pidfile)
86
+ when :running, :not_owned
87
+ puts "A server is already running. Check #{pidfile}"
88
+ exit(1)
89
+ when :dead
90
+ File.delete(pidfile)
83
91
  end
84
92
  end
85
93
 
86
94
  def pid_status(pidfile)
87
- return :exited unless File.exists?(pidfile)
95
+ return :exited unless File.exist?(pidfile)
96
+
88
97
  pid = ::File.read(pidfile).to_i
89
- return :dead if pid == 0
98
+ return :dead if pid.zero?
99
+
90
100
  Process.kill(0, pid)
91
101
  :running
92
102
  rescue Errno::ESRCH
@@ -1,19 +1,14 @@
1
1
  module Oxidized::Config::Vars
2
2
  # convenience method for accessing node, group or global level user variables
3
- # nil values will be ignored
4
- def vars name
5
- r = @node.vars[name] unless @node.vars.nil?
6
- if Oxidized.config.groups.has_key?(@node.group)
7
- if Oxidized.config.groups[@node.group].vars.has_key?(name.to_s)
8
- r ||= Oxidized.config.groups[@node.group].vars[name.to_s]
9
- end
3
+ def vars(name)
4
+ if @node.vars&.has_key?(name)
5
+ @node.vars[name]
6
+ elsif Oxidized.config.groups.has_key?(@node.group) && Oxidized.config.groups[@node.group].vars.has_key?(name.to_s)
7
+ Oxidized.config.groups[@node.group].vars[name.to_s]
8
+ elsif Oxidized.config.models.has_key(@node.model.class.name.to_s.downcase) && Oxidized.config.models[@node.model.class.name.to_s.downcase].vars.has_key?(name.to_s)
9
+ Oxidized.config.models[@node.model.class.name.to_s.downcase].vars[name.to_s]
10
+ elsif Oxidized.config.vars.has_key?(name.to_s)
11
+ Oxidized.config.vars[name.to_s]
10
12
  end
11
- if Oxidized.config.models.has_key?(@node.model.class.name.to_s.downcase)
12
- if Oxidized.config.models[@node.model.class.name.to_s.downcase].vars.has_key?(name.to_s)
13
- r ||= Oxidized.config.models[@node.model.class.name.to_s.downcase].vars[name.to_s]
14
- end
15
- end
16
- r ||= Oxidized.config.vars[name.to_s] if Oxidized.config.vars.has_key?(name.to_s)
17
- r
18
13
  end
19
14
  end
@@ -4,22 +4,23 @@ module Oxidized
4
4
  class InvalidConfig < OxidizedError; end
5
5
  class Config
6
6
  Root = ENV['OXIDIZED_HOME'] || File.join(ENV['HOME'], '.config', 'oxidized')
7
- Crash = File.join Root, 'crash'
8
- Log = File.join Root, 'logs'
9
- InputDir = File.join Directory, %w(lib oxidized input)
10
- OutputDir = File.join Directory, %w(lib oxidized output)
11
- ModelDir = File.join Directory, %w(lib oxidized model)
12
- SourceDir = File.join Directory, %w(lib oxidized source)
13
- HookDir = File.join Directory, %w(lib oxidized hook)
7
+ Crash = File.join(ENV['OXIDIZED_LOGS'] || Root, 'crash')
8
+ Log = File.join(ENV['OXIDIZED_LOGS'] || Root, 'logs')
9
+ InputDir = File.join Directory, %w[lib oxidized input]
10
+ OutputDir = File.join Directory, %w[lib oxidized output]
11
+ ModelDir = File.join Directory, %w[lib oxidized model]
12
+ SourceDir = File.join Directory, %w[lib oxidized source]
13
+ HookDir = File.join Directory, %w[lib oxidized hook]
14
14
  Sleep = 1
15
15
 
16
- def self.load(cmd_opts={})
16
+ def self.load(cmd_opts = {})
17
17
  asetus = Asetus.new(name: 'oxidized', load: false, key_to_s: true)
18
18
  Oxidized.asetus = asetus
19
19
 
20
20
  asetus.default.username = 'username'
21
21
  asetus.default.password = 'password'
22
22
  asetus.default.model = 'junos'
23
+ asetus.default.resolve_dns = true # if false, don't resolve DNS to IP
23
24
  asetus.default.interval = 3600
24
25
  asetus.default.use_syslog = false
25
26
  asetus.default.debug = false
@@ -34,21 +35,27 @@ module Oxidized
34
35
  asetus.default.models = {} # model level configuration
35
36
  asetus.default.pid = File.join(Oxidized::Config::Root, 'pid')
36
37
 
37
- asetus.default.input.default = 'ssh, telnet'
38
- asetus.default.input.debug = false # or String for session log file
39
- asetus.default.input.ssh.secure = false # complain about changed certs
38
+ asetus.default.crash.directory = File.join(Oxidized::Config::Root, 'crashes')
39
+ asetus.default.crash.hostnames = false
40
+
41
+ asetus.default.stats.history_size = 10
42
+ asetus.default.input.default = 'ssh, telnet'
43
+ asetus.default.input.debug = false # or String for session log file
44
+ asetus.default.input.ssh.secure = false # complain about changed certs
45
+ asetus.default.input.ftp.passive = true # ftp passive mode
46
+ asetus.default.input.utf8_encoded = true # configuration is utf8 encoded or ascii-8bit
40
47
 
41
48
  asetus.default.output.default = 'file' # file, git
42
49
  asetus.default.source.default = 'csv' # csv, sql
43
50
 
44
51
  asetus.default.model_map = {
45
- 'cisco' => 'ios',
46
52
  'juniper' => 'junos',
53
+ 'cisco' => 'ios'
47
54
  }
48
55
 
49
56
  begin
50
57
  asetus.load # load system+user configs, merge to Config.cfg
51
- rescue => error
58
+ rescue StandardError => error
52
59
  raise InvalidConfig, "Error loading config: #{error.message}"
53
60
  end
54
61
 
data/lib/oxidized/core.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  module Oxidized
2
2
  class << self
3
- def new *args
3
+ def new(*args)
4
4
  Core.new args
5
5
  end
6
6
  end
@@ -8,12 +8,13 @@ module Oxidized
8
8
  class Core
9
9
  class NoNodesFound < OxidizedError; end
10
10
 
11
- def initialize args
11
+ def initialize(_args)
12
12
  Oxidized.mgr = Manager.new
13
13
  Oxidized.Hooks = HookManager.from_config(Oxidized.config)
14
- nodes = Nodes.new
15
- raise NoNodesFound, 'source returns no usable nodes' if nodes.size == 0
16
- @worker = Worker.new nodes
14
+ nodes = Nodes.new
15
+ raise NoNodesFound, 'source returns no usable nodes' if nodes.size.zero?
16
+
17
+ @worker = Worker.new nodes
17
18
  trap('HUP') { nodes.load }
18
19
  if Oxidized.config.rest?
19
20
  begin
@@ -22,7 +23,7 @@ module Oxidized
22
23
  raise OxidizedError, 'oxidized-web not found: sudo gem install oxidized-web - \
23
24
  or disable web support by setting "rest: false" in your configuration'
24
25
  end
25
- @rest = API::Web.new nodes, Oxidized.config.rest
26
+ @rest = API::Web.new nodes, Oxidized.config.rest
26
27
  @rest.run
27
28
  end
28
29
  run
@@ -32,10 +33,7 @@ module Oxidized
32
33
 
33
34
  def run
34
35
  Oxidized.logger.debug "lib/oxidized/core.rb: Starting the worker..."
35
- while true
36
- @worker.work
37
- sleep Config::Sleep
38
- end
36
+ @worker.work while sleep Config::Sleep
39
37
  end
40
38
  end
41
39
  end
@@ -10,18 +10,17 @@ class AwsSns < Oxidized::Hook
10
10
  sns = Aws::SNS::Resource.new(region: cfg.region)
11
11
  topic = sns.topic(cfg.topic_arn)
12
12
  message = {
13
- :event => ctx.event.to_s
13
+ event: ctx.event.to_s
14
14
  }
15
15
  if ctx.node
16
16
  message.merge!(
17
- :group => ctx.node.group.to_s,
18
- :model => ctx.node.model.class.name.to_s.downcase,
19
- :node => ctx.node.name.to_s
17
+ group: ctx.node.group.to_s,
18
+ model: ctx.node.model.class.name.to_s.downcase,
19
+ node: ctx.node.name.to_s
20
20
  )
21
21
  end
22
- topic.publish({
22
+ topic.publish(
23
23
  message: message.to_json
24
- })
24
+ )
25
25
  end
26
-
27
26
  end
@@ -0,0 +1,43 @@
1
+ require 'cisco_spark'
2
+
3
+ # defaults to posting a diff, if messageformat is supplied them a message will be posted too
4
+ # diff defaults to true
5
+ # Modified from slackdiff
6
+
7
+ class CiscoSparkDiff < Oxidized::Hook
8
+ def validate_cfg!
9
+ raise KeyError, 'hook.accesskey is required' unless cfg.has_key?('accesskey')
10
+ raise KeyError, 'hook.space is required' unless cfg.has_key?('space')
11
+ end
12
+
13
+ def run_hook(ctx)
14
+ return unless ctx.node
15
+ return unless ctx.event.to_s == "post_store"
16
+
17
+ log "Connecting to Cisco Spark"
18
+ CiscoSpark.configure do |config|
19
+ config.api_key = cfg.accesskey
20
+ config.proxy = cfg.proxy if cfg.has_key?('proxy')
21
+ end
22
+ room = CiscoSpark::Room.new(id: cfg.space)
23
+ log "Connected"
24
+
25
+ if cfg.has_key?("diff") ? cfg.diff : true
26
+ gitoutput = ctx.node.output.new
27
+ diff = gitoutput.get_diff ctx.node, ctx.node.group, ctx.commitref, nil
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)
31
+ end
32
+
33
+ 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}"
38
+ room.send_message CiscoSpark::Message.new(text: msg)
39
+ end
40
+
41
+ log "Finished"
42
+ end
43
+ end
@@ -12,40 +12,37 @@ class Exec < Oxidized::Hook
12
12
  if cfg.has_key? "timeout"
13
13
  @timeout = cfg.timeout
14
14
  raise "invalid timeout value" unless @timeout.is_a?(Integer) &&
15
- @timeout > 0
15
+ @timeout.positive?
16
16
  end
17
17
 
18
- if cfg.has_key? "async"
19
- @async = !!cfg.async
20
- end
18
+ @async = !!cfg.async if cfg.has_key? "async"
21
19
 
22
20
  if cfg.has_key? "cmd"
23
21
  @cmd = cfg.cmd
24
22
  raise "invalid cmd value" unless @cmd.is_a?(String) || @cmd.is_a?(Array)
25
23
  end
26
-
27
24
  rescue RuntimeError => e
28
25
  raise ArgumentError,
29
- "#{self.class.name}: configuration invalid: #{e.message}"
26
+ "#{self.class.name}: configuration invalid: #{e.message}"
30
27
  end
31
28
 
32
- def run_hook ctx
29
+ def run_hook(ctx)
33
30
  env = make_env ctx
34
31
  log "Execute: #{@cmd.inspect}", :debug
35
32
  th = Thread.new do
36
33
  begin
37
34
  run_cmd! env
38
- rescue => e
35
+ rescue StandardError => e
39
36
  raise e unless @async
40
37
  end
41
38
  end
42
39
  th.join unless @async
43
40
  end
44
41
 
45
- def run_cmd! env
42
+ def run_cmd!(env)
46
43
  pid, status = nil, nil
47
44
  Timeout.timeout(@timeout) do
48
- pid = spawn env, @cmd , :unsetenv_others => true
45
+ pid = spawn env, @cmd, unsetenv_others: true
49
46
  pid, status = wait2 pid
50
47
  unless status.exitstatus.zero?
51
48
  msg = "#{@cmd.inspect} failed with exit value #{status.exitstatus}"
@@ -53,34 +50,32 @@ class Exec < Oxidized::Hook
53
50
  raise msg
54
51
  end
55
52
  end
56
- rescue TimeoutError
53
+ rescue Timeout::Error
57
54
  kill "TERM", pid
58
55
  msg = "#{@cmd} timed out"
59
56
  log msg, :error
60
- raise TimeoutError, msg
57
+ raise Timeout::Error, msg
61
58
  end
62
59
 
63
- def make_env ctx
60
+ def make_env(ctx)
64
61
  env = {
65
62
  "OX_EVENT" => ctx.event.to_s
66
63
  }
67
64
  if ctx.node
68
65
  env.merge!(
69
- "OX_NODE_NAME" => ctx.node.name.to_s,
70
- "OX_NODE_IP" => ctx.node.ip.to_s,
71
- "OX_NODE_FROM" => ctx.node.from.to_s,
72
- "OX_NODE_MSG" => ctx.node.msg.to_s,
73
- "OX_NODE_GROUP" => ctx.node.group.to_s,
74
- "OX_EVENT" => ctx.event.to_s,
66
+ "OX_NODE_NAME" => ctx.node.name.to_s,
67
+ "OX_NODE_IP" => ctx.node.ip.to_s,
68
+ "OX_NODE_FROM" => ctx.node.from.to_s,
69
+ "OX_NODE_MSG" => ctx.node.msg.to_s,
70
+ "OX_NODE_GROUP" => ctx.node.group.to_s,
71
+ "OX_NODE_MODEL" => ctx.node.model.class.name,
75
72
  "OX_REPO_COMMITREF" => ctx.commitref.to_s,
76
- "OX_REPO_NAME" => ctx.node.repo.to_s,
73
+ "OX_REPO_NAME" => ctx.node.repo.to_s
77
74
  )
78
75
  end
79
76
  if ctx.job
80
- env.merge!(
81
- "OX_JOB_STATUS" => ctx.job.status.to_s,
82
- "OX_JOB_TIME" => ctx.job.time.to_s,
83
- )
77
+ env["OX_JOB_STATUS"] = ctx.job.status.to_s
78
+ env["OX_JOB_TIME"] = ctx.job.time.to_s
84
79
  end
85
80
  env
86
81
  end
@@ -18,8 +18,8 @@ class GithubRepo < Oxidized::Hook
18
18
  result = repo.fetch('origin', [repo.head.name], credentials: credentials)
19
19
  log result.inspect, :debug
20
20
 
21
- unless result[:total_deltas] > 0
22
- log "nothing recieved after fetch", :debug
21
+ unless result[:total_deltas].positive?
22
+ log "nothing received after fetch", :debug
23
23
  return
24
24
  end
25
25
 
@@ -34,27 +34,27 @@ class GithubRepo < Oxidized::Hook
34
34
  return
35
35
  end
36
36
 
37
- Rugged::Commit.create(repo, {
38
- parents: [repo.head.target, their_branch.target],
39
- tree: merge_index.write_tree(repo),
40
- message: "Merge remote-tracking branch '#{their_branch.name}'",
41
- update_ref: "HEAD"
42
- })
37
+ Rugged::Commit.create(repo,
38
+ parents: [repo.head.target, their_branch.target],
39
+ tree: merge_index.write_tree(repo),
40
+ message: "Merge remote-tracking branch '#{their_branch.name}'",
41
+ update_ref: "HEAD")
43
42
  end
44
43
 
45
44
  private
46
45
 
47
46
  def credentials
48
- @credentials ||= if cfg.has_key?('username') && cfg.has_key?('password')
49
- log "Using https auth", :debug
50
- Rugged::Credentials::UserPassword.new(username: cfg.username, password: cfg.password)
51
- else
52
- if cfg.has_key?('publickey') && cfg.has_key?('privatekey')
53
- log "Using ssh auth with key", :debug
54
- Rugged::Credentials::SshKey.new(username: 'git', publickey: File.expand_path(cfg.publickey), privatekey: File.expand_path(cfg.privatekey))
47
+ Proc.new do |_url, username_from_url, _allowed_types| # rubocop:disable Style/Proc
48
+ git_user = cfg.has_key?('username') ? cfg.username : (username_from_url || 'git')
49
+ if cfg.has_key?('password')
50
+ log "Authenticating using username and password as '#{git_user}'", :debug
51
+ Rugged::Credentials::UserPassword.new(username: git_user, password: cfg.password)
52
+ elsif cfg.has_key?('publickey') && cfg.has_key?('privatekey')
53
+ log "Authenticating using ssh keys as '#{git_user}'", :debug
54
+ Rugged::Credentials::SshKey.new(username: git_user, publickey: File.expand_path(cfg.publickey), privatekey: File.expand_path(cfg.privatekey), passphrase: ENV["OXIDIZED_SSH_PASSPHRASE"])
55
55
  else
56
- log "Using ssh auth with agentforwarding", :debug
57
- Rugged::Credentials::SshKeyFromAgent.new(username: 'git')
56
+ log "Authenticating using ssh agent as '#{git_user}'", :debug
57
+ Rugged::Credentials::SshKeyFromAgent.new(username: git_user)
58
58
  end
59
59
  end
60
60
  end
@@ -3,7 +3,7 @@ class NoopHook < Oxidized::Hook
3
3
  log "Validate config"
4
4
  end
5
5
 
6
- def run_hook ctx
6
+ def run_hook(ctx)
7
7
  log "Run hook with context: #{ctx}"
8
8
  end
9
9
  end