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
@@ -1,54 +1,60 @@
1
1
  module Oxidized
2
- class CSV < Source
3
- def initialize
4
- @cfg = Oxidized.config.source.csv
5
- super
6
- end
7
-
8
- def setup
9
- if @cfg.empty?
10
- Oxidized.asetus.user.source.csv.file = File.join(Config::Root, 'router.db')
11
- Oxidized.asetus.user.source.csv.delimiter = /:/
12
- Oxidized.asetus.user.source.csv.map.name = 0
13
- Oxidized.asetus.user.source.csv.map.model = 1
14
- Oxidized.asetus.user.source.csv.gpg = false
15
- Oxidized.asetus.save :user
16
- raise NoConfig, 'no source csv config, edit ~/.config/oxidized/config'
2
+ class CSV < Source
3
+ def initialize
4
+ @cfg = Oxidized.config.source.csv
5
+ super
17
6
  end
18
- require 'gpgme' if @cfg.gpg?
19
- end
20
7
 
21
- def load
22
- nodes = []
23
- file = File.expand_path(@cfg.file)
24
- file = if @cfg.gpg?
25
- crypto = GPGME::Crypto.new password: @cfg.gpg_password
26
- crypto.decrypt(file).to_s
27
- else
28
- open(file)
29
- end
30
- file.each_line do |line|
31
- next if line.match(/^\s*#/)
32
- data = line.chomp.split(@cfg.delimiter, -1)
33
- next if data.empty?
34
- # map node parameters
35
- keys = {}
36
- @cfg.map.each do |key, position|
37
- keys[key.to_sym] = node_var_interpolate data[position]
8
+ def setup
9
+ if @cfg.empty?
10
+ Oxidized.asetus.user.source.csv.file = File.join(Config::Root, 'router.db')
11
+ Oxidized.asetus.user.source.csv.delimiter = /:/
12
+ Oxidized.asetus.user.source.csv.map.name = 0
13
+ Oxidized.asetus.user.source.csv.map.model = 1
14
+ Oxidized.asetus.user.source.csv.gpg = false
15
+ Oxidized.asetus.save :user
16
+ raise NoConfig, 'no source csv config, edit ~/.config/oxidized/config'
38
17
  end
39
- keys[:model] = map_model keys[:model] if keys.key? :model
18
+ require 'gpgme' if @cfg.gpg?
19
+ end
20
+
21
+ def load(_node_want = nil)
22
+ nodes = []
23
+ open_file.each_line do |line|
24
+ next if line =~ /^\s*#/
25
+
26
+ data = line.chomp.split(@cfg.delimiter, -1)
27
+ next if data.empty?
28
+
29
+ # map node parameters
30
+ keys = {}
31
+ @cfg.map.each do |key, position|
32
+ keys[key.to_sym] = node_var_interpolate data[position]
33
+ end
34
+ keys[:model] = map_model keys[:model] if keys.has_key? :model
40
35
 
41
- # map node specific vars
42
- vars = {}
43
- @cfg.vars_map.each do |key, position|
44
- vars[key.to_sym] = node_var_interpolate data[position]
36
+ # map node specific vars
37
+ vars = {}
38
+ @cfg.vars_map.each do |key, position|
39
+ vars[key.to_sym] = node_var_interpolate data[position]
40
+ end
41
+ keys[:vars] = vars unless vars.empty?
42
+
43
+ nodes << keys
45
44
  end
46
- keys[:vars] = vars unless vars.empty?
45
+ nodes
46
+ end
47
+
48
+ private
47
49
 
48
- nodes << keys
50
+ def open_file
51
+ file = File.expand_path(@cfg.file)
52
+ if @cfg.gpg?
53
+ crypto = GPGME::Crypto.new password: @cfg.gpg_password
54
+ crypto.decrypt(File.open(file)).to_s
55
+ else
56
+ File.open(file)
57
+ end
49
58
  end
50
- nodes
51
59
  end
52
-
53
- end
54
60
  end
@@ -1,77 +1,77 @@
1
1
  module Oxidized
2
- class HTTP < Source
3
- def initialize
4
- @cfg = Oxidized.config.source.http
5
- super
6
- end
2
+ class HTTP < Source
3
+ def initialize
4
+ @cfg = Oxidized.config.source.http
5
+ super
6
+ end
7
+
8
+ def setup
9
+ return unless @cfg.url.empty?
7
10
 
8
- def setup
9
- if @cfg.url.empty?
10
11
  raise NoConfig, 'no source http url config, edit ~/.config/oxidized/config'
11
12
  end
12
- end
13
13
 
14
- require "net/http"
15
- require "uri"
16
- require "json"
14
+ require "net/http"
15
+ require "net/https"
16
+ require "uri"
17
+ require "json"
17
18
 
18
- def load
19
- nodes = []
20
- uri = URI.parse(@cfg.url)
21
- http = Net::HTTP.new(uri.host, uri.port)
22
- http.use_ssl = true if uri.scheme == 'https'
23
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE unless @cfg.secure
19
+ def load(node_want = nil)
20
+ nodes = []
21
+ data = JSON.parse(read_http(node_want))
22
+ data = string_navigate(data, @cfg.hosts_location) if @cfg.hosts_location?
23
+ data.each do |node|
24
+ next if node.empty?
24
25
 
25
- # map headers
26
- headers = {}
27
- @cfg.headers.each do |header, value|
28
- headers[header] = value
29
- end
26
+ # map node parameters
27
+ keys = {}
28
+ @cfg.map.each do |key, want_position|
29
+ keys[key.to_sym] = node_var_interpolate string_navigate(node, want_position)
30
+ end
31
+ keys[:model] = map_model keys[:model] if keys.has_key? :model
30
32
 
31
- request = Net::HTTP::Get.new(uri.request_uri, headers)
32
- if (@cfg.user? && @cfg.pass?)
33
- request.basic_auth(@cfg.user,@cfg.pass)
34
- end
33
+ # map node specific vars
34
+ vars = {}
35
+ @cfg.vars_map.each do |key, want_position|
36
+ vars[key.to_sym] = node_var_interpolate string_navigate(node, want_position)
37
+ end
38
+ keys[:vars] = vars unless vars.empty?
35
39
 
36
- response = http.request(request)
37
- data = JSON.parse(response.body)
38
- data.each do |node|
39
- next if node.empty?
40
- # map node parameters
41
- keys = {}
42
- @cfg.map.each do |key, want_position|
43
- want_positions = want_position.split('.')
44
- keys[key.to_sym] = node_var_interpolate node.dig(*want_positions)
40
+ nodes << keys
45
41
  end
46
- keys[:model] = map_model keys[:model] if keys.key? :model
42
+ nodes
43
+ end
47
44
 
48
- # map node specific vars
49
- vars = {}
50
- @cfg.vars_map.each do |key, want_position|
51
- want_positions = want_position.split('.')
52
- vars[key.to_sym] = node_var_interpolate node.dig(*want_positions)
53
- end
54
- keys[:vars] = vars unless vars.empty?
45
+ private
55
46
 
56
- nodes << keys
47
+ def string_navigate(object, wants)
48
+ wants = wants.split(".").map do |want|
49
+ head, match, _tail = want.partition(/\[\d+\]/)
50
+ match.empty? ? head : [head, match[1..-2].to_i]
51
+ end
52
+ wants.flatten.each do |want|
53
+ object = object[want] if object.respond_to? :each
54
+ end
55
+ object
57
56
  end
58
- nodes
59
- end
60
57
 
61
- end
62
- end
58
+ def read_http(node_want)
59
+ uri = URI.parse(@cfg.url)
60
+ http = Net::HTTP.new(uri.host, uri.port)
61
+ http.use_ssl = true if uri.scheme == 'https'
62
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE unless @cfg.secure
63
63
 
64
- if RUBY_VERSION < '2.3'
65
- class Hash
66
- def dig(key, *rest)
67
- value = self[key]
68
- if value.nil? || rest.empty?
69
- value
70
- elsif value.respond_to?(:dig)
71
- value.dig(*rest)
72
- else # foo.bar.baz (bar exist but is not hash)
73
- return nil
64
+ # map headers
65
+ headers = {}
66
+ @cfg.headers.each do |header, value|
67
+ headers[header] = value
74
68
  end
69
+
70
+ req_uri = uri.request_uri
71
+ req_uri = "#{req_uri}/#{node_want}" if node_want
72
+ request = Net::HTTP::Get.new(req_uri, headers)
73
+ request.basic_auth(@cfg.user, @cfg.pass) if @cfg.user? && @cfg.pass?
74
+ http.request(request).body
75
75
  end
76
76
  end
77
77
  end
@@ -3,21 +3,20 @@ module Oxidized
3
3
  class NoConfig < OxidizedError; end
4
4
 
5
5
  def initialize
6
- @map = (Oxidized.config.model_map or {})
6
+ @map = (Oxidized.config.model_map || {})
7
7
  end
8
8
 
9
- def map_model model
9
+ def map_model(model)
10
10
  @map.has_key?(model) ? @map[model] : model
11
11
  end
12
12
 
13
- def node_var_interpolate var
14
- case var
15
- when "nil" then nil
16
- when "false" then false
17
- when "true" then true
18
- else var
19
- end
13
+ def node_var_interpolate(var)
14
+ case var
15
+ when "nil" then nil
16
+ when "false" then false
17
+ when "true" then true
18
+ else var
19
+ end
20
20
  end
21
-
22
21
  end
23
22
  end
@@ -1,13 +1,14 @@
1
1
  module Oxidized
2
- class SQL < Source
3
- begin
4
- require 'sequel'
5
- rescue LoadError
6
- raise OxidizedError, 'sequel not found: sudo gem install sequel'
7
- end
2
+ class SQL < Source
3
+ begin
4
+ require 'sequel'
5
+ rescue LoadError
6
+ raise OxidizedError, 'sequel not found: sudo gem install sequel'
7
+ end
8
+
9
+ def setup
10
+ return unless @cfg.empty?
8
11
 
9
- def setup
10
- if @cfg.empty?
11
12
  Oxidized.asetus.user.source.sql.adapter = 'sqlite'
12
13
  Oxidized.asetus.user.source.sql.database = File.join(Config::Root, 'sqlite.db')
13
14
  Oxidized.asetus.user.source.sql.table = 'devices'
@@ -16,48 +17,49 @@ class SQL < Source
16
17
  Oxidized.asetus.save :user
17
18
  raise NoConfig, 'no source sql config, edit ~/.config/oxidized/config'
18
19
  end
19
- end
20
20
 
21
- def load
22
- nodes = []
23
- db = connect
24
- query = db[@cfg.table.to_sym]
25
- query = query.with_sql(@cfg.query) if @cfg.query?
26
- query.each do |node|
27
- # map node parameters
28
- keys = {}
29
- @cfg.map.each { |key, sql_column| keys[key.to_sym] = node_var_interpolate node[sql_column.to_sym] }
30
- keys[:model] = map_model keys[:model] if keys.key? :model
31
-
32
- # map node specific vars
33
- vars = {}
34
- @cfg.vars_map.each do |key, sql_column|
35
- vars[key.to_sym] = node_var_interpolate node[sql_column.to_sym]
36
- end
37
- keys[:vars] = vars unless vars.empty?
21
+ def load(node_want = nil)
22
+ nodes = []
23
+ db = connect
24
+ query = db[@cfg.table.to_sym]
25
+ query = query.with_sql(@cfg.query) if @cfg.query?
26
+
27
+ query = query.where(@cfg.map.name.to_sym => node_want) if node_want
28
+
29
+ query.each do |node|
30
+ # map node parameters
31
+ keys = {}
32
+ @cfg.map.each { |key, sql_column| keys[key.to_sym] = node_var_interpolate node[sql_column.to_sym] }
33
+ keys[:model] = map_model keys[:model] if keys.has_key? :model
34
+
35
+ # map node specific vars
36
+ vars = {}
37
+ @cfg.vars_map.each do |key, sql_column|
38
+ vars[key.to_sym] = node_var_interpolate node[sql_column.to_sym]
39
+ end
40
+ keys[:vars] = vars unless vars.empty?
38
41
 
39
- nodes << keys
42
+ nodes << keys
43
+ end
44
+ db.disconnect
45
+ nodes
40
46
  end
41
- db.disconnect
42
- nodes
43
- end
44
47
 
45
- private
48
+ private
46
49
 
47
- def initialize
48
- super
49
- @cfg = Oxidized.config.source.sql
50
- end
50
+ def initialize
51
+ super
52
+ @cfg = Oxidized.config.source.sql
53
+ end
51
54
 
52
- def connect
53
- Sequel.connect(:adapter => @cfg.adapter,
54
- :host => @cfg.host?,
55
- :user => @cfg.user?,
56
- :password => @cfg.password?,
57
- :database => @cfg.database)
58
- rescue Sequel::AdapterNotFound => error
59
- raise OxidizedError, "SQL adapter gem not installed: " + error.message
55
+ def connect
56
+ Sequel.connect(adapter: @cfg.adapter,
57
+ host: @cfg.host?,
58
+ user: @cfg.user?,
59
+ password: @cfg.password?,
60
+ database: @cfg.database)
61
+ rescue Sequel::AdapterNotFound => error
62
+ raise OxidizedError, "SQL adapter gem not installed: " + error.message
63
+ end
60
64
  end
61
-
62
- end
63
65
  end
@@ -4,29 +4,33 @@ module Oxidized
4
4
  attr_accessor :type, :cmd, :name
5
5
 
6
6
  # @return [Oxidized::String] copy of self with last line removed
7
- def cut_tail
8
- Oxidized::String.new each_line.to_a[0..-2].join
7
+ def cut_tail(lines = 1)
8
+ Oxidized::String.new each_line.to_a[0..-1 - lines].join
9
9
  end
10
10
 
11
11
  # @return [Oxidized::String] copy of self with first line removed
12
- def cut_head
13
- Oxidized::String.new each_line.to_a[1..-1].join
12
+ def cut_head(lines = 1)
13
+ Oxidized::String.new each_line.to_a[lines..-1].join
14
+ end
15
+
16
+ # @return [Oxidized::String] copy of self with first and last lines removed
17
+ def cut_both(head = 1, tail = 1)
18
+ Oxidized::String.new each_line.to_a[head..-1 - tail].join
14
19
  end
15
20
 
16
21
  # sets @cmd and @name unless @name is already set
17
- def set_cmd command
18
- @cmd = command
19
- @name ||= @cmd.strip.gsub(/\s+/, '_')
22
+ def set_cmd(command)
23
+ @cmd = command
24
+ @name ||= @cmd.to_s.strip.gsub(/\s+/, '_') # what to do when command is proc? #to_s seems ghetto
20
25
  end
21
26
 
22
- def initialize str=''
27
+ def initialize(str = '')
23
28
  super
24
- if str.class == Oxidized::String
25
- @cmd = str.cmd
26
- @name = str.name
27
- @type = str.type
28
- end
29
- end
29
+ return unless str.class == Oxidized::String
30
30
 
31
+ @cmd = str.cmd
32
+ @name = str.name
33
+ @type = str.type
34
+ end
31
35
  end
32
36
  end
@@ -1,3 +1,19 @@
1
1
  module Oxidized
2
- VERSION = '0.20.0'
2
+ VERSION = '0.28.0'.freeze
3
+ VERSION_FULL = '0.28.0'.freeze
4
+ def self.version_set
5
+ version_full = %x(git describe --tags).chop rescue ""
6
+ version = %x(git describe --tags --abbrev=0).chop rescue ""
7
+
8
+ return false unless [version, version_full].none?(&:empty?)
9
+
10
+ Oxidized.send(:remove_const, :VERSION)
11
+ Oxidized.send(:remove_const, :VERSION_FULL)
12
+ const_set(:VERSION, version)
13
+ const_set(:VERSION_FULL, version_full)
14
+ file = File.readlines(__FILE__)
15
+ file[1] = " VERSION = '%s'.freeze\n" % VERSION
16
+ file[2] = " VERSION_FULL = '%s'.freeze\n" % VERSION_FULL
17
+ File.write(__FILE__, file.join)
18
+ end
3
19
  end
@@ -2,7 +2,8 @@ module Oxidized
2
2
  require 'oxidized/job'
3
3
  require 'oxidized/jobs'
4
4
  class Worker
5
- def initialize nodes
5
+ def initialize(nodes)
6
+ @jobs_done = 0
6
7
  @nodes = nodes
7
8
  @jobs = Jobs.new(Oxidized.config.threads, Oxidized.config.interval, @nodes)
8
9
  @nodes.jobs = @jobs
@@ -11,65 +12,103 @@ module Oxidized
11
12
 
12
13
  def work
13
14
  ended = []
14
- @jobs.delete_if { |job| ended << job if not job.alive? }
15
+ @jobs.delete_if { |job| ended << job unless job.alive? }
15
16
  ended.each { |job| process job }
16
17
  @jobs.work
18
+
17
19
  while @jobs.size < @jobs.want
18
- Oxidized.logger.debug "lib/oxidized/worker.rb: Jobs #{@jobs.size}, Want: #{@jobs.want}"
20
+ Oxidized.logger.debug "lib/oxidized/worker.rb: Jobs running: #{@jobs.size} of #{@jobs.want} - ended: #{@jobs_done} of #{@nodes.size}"
19
21
  # ask for next node in queue non destructive way
20
22
  nextnode = @nodes.first
21
23
  unless nextnode.last.nil?
22
24
  # Set unobtainable value for 'last' if interval checking is disabled
23
- last = Oxidized.config.interval == 0 ? Time.now.utc + 10 : nextnode.last.end
25
+ last = Oxidized.config.interval.zero? ? Time.now.utc + 10 : nextnode.last.end
24
26
  break if last + Oxidized.config.interval > Time.now.utc
25
27
  end
26
28
  # shift nodes and get the next node
27
29
  node = @nodes.get
28
30
  node.running? ? next : node.running = true
31
+
29
32
  @jobs.push Job.new node
30
- Oxidized.logger.debug "lib/oxidized/worker.rb: Added #{node.name} to the job queue"
33
+ Oxidized.logger.debug "lib/oxidized/worker.rb: Added #{node.group}/#{node.name} to the job queue"
31
34
  end
35
+
36
+ run_done_hook if cycle_finished?
32
37
  Oxidized.logger.debug("lib/oxidized/worker.rb: #{@jobs.size} jobs running in parallel") unless @jobs.empty?
33
38
  end
34
39
 
35
- def process job
40
+ def process(job)
36
41
  node = job.node
37
42
  node.last = job
38
43
  node.stats.add job
39
44
  @jobs.duration job.time
40
45
  node.running = false
41
46
  if job.status == :success
42
- Oxidized.Hooks.handle :node_success, :node => node,
43
- :job => job
44
- msg = "update #{node.name}"
45
- msg += " from #{node.from}" if node.from
46
- msg += " with message '#{node.msg}'" if node.msg
47
- output = node.output.new
48
- if output.store node.name, job.config,
49
- :msg => msg, :user => node.user, :group => node.group
50
- Oxidized.logger.info "Configuration updated for #{node.group}/#{node.name}"
51
- Oxidized.Hooks.handle :post_store, :node => node,
52
- :job => job,
53
- :commitref => output.commitref
54
- end
55
- node.reset
47
+ process_success node, job
56
48
  else
57
- msg = "#{node.name} status #{job.status}"
58
- if node.retry < Oxidized.config.retries
59
- node.retry += 1
60
- msg += ", retry attempt #{node.retry}"
61
- @nodes.next node.name
62
- else
63
- msg += ", retries exhausted, giving up"
64
- node.retry = 0
65
- Oxidized.Hooks.handle :node_fail, :node => node,
66
- :job => job
67
- end
68
- Oxidized.logger.warn msg
49
+ process_failure node, job
69
50
  end
70
51
  rescue NodeNotFound
71
- Oxidized.logger.warn "#{node.name} not found, removed while collecting?"
52
+ Oxidized.logger.warn "#{node.group}/#{node.name} not found, removed while collecting?"
53
+ end
54
+
55
+ private
56
+
57
+ def process_success(node, job)
58
+ @jobs_done += 1 # needed for :nodes_done hook
59
+ Oxidized.Hooks.handle :node_success, node: node,
60
+ job: job
61
+ msg = "update #{node.group}/#{node.name}"
62
+ msg += " from #{node.from}" if node.from
63
+ msg += " with message '#{node.msg}'" if node.msg
64
+ output = node.output.new
65
+ if output.store node.name, job.config,
66
+ msg: msg, email: node.email, user: node.user, group: node.group
67
+ node.modified
68
+ Oxidized.logger.info "Configuration updated for #{node.group}/#{node.name}"
69
+ Oxidized.Hooks.handle :post_store, node: node,
70
+ job: job,
71
+ commitref: output.commitref
72
+ end
73
+ node.reset
72
74
  end
73
75
 
76
+ def process_failure(node, job)
77
+ msg = "#{node.group}/#{node.name} status #{job.status}"
78
+ if node.retry < Oxidized.config.retries
79
+ node.retry += 1
80
+ msg += ", retry attempt #{node.retry}"
81
+ @nodes.next node.name
82
+ else
83
+ # Only increment the @jobs_done when we give up retries for a node (or success).
84
+ # As it would otherwise cause @jobs_done to be incremented with generic retries.
85
+ # This would cause :nodes_done hook to desync from running at the end of the nodelist and
86
+ # be fired when the @jobs_done > @nodes.count (could be mid-cycle on the next cycle).
87
+ @jobs_done += 1
88
+ msg += ", retries exhausted, giving up"
89
+ node.retry = 0
90
+ Oxidized.Hooks.handle :node_fail, node: node,
91
+ job: job
92
+ end
93
+ Oxidized.logger.warn msg
94
+ end
95
+
96
+ def cycle_finished?
97
+ if @jobs_done > @nodes.count
98
+ true
99
+ else
100
+ @jobs_done.positive? && (@jobs_done % @nodes.count).zero?
101
+ end
102
+ end
103
+
104
+ def run_done_hook
105
+ Oxidized.logger.debug "lib/oxidized/worker.rb: Running :nodes_done hook"
106
+ Oxidized.Hooks.handle :nodes_done
107
+ rescue StandardError => e
108
+ # swallow the hook erros and continue as normal
109
+ Oxidized.logger.error "lib/oxidized/worker.rb: #{e.message}"
110
+ ensure
111
+ @jobs_done = 0
112
+ end
74
113
  end
75
114
  end
data/oxidized.gemspec CHANGED
@@ -1,37 +1,38 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
1
+ lib = File.expand_path('lib', __dir__)
3
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
3
  require 'oxidized/version'
5
4
 
6
5
  Gem::Specification.new do |s|
7
6
  s.name = 'oxidized'
8
7
  s.version = Oxidized::VERSION
9
- s.licenses = %w( Apache-2.0 )
8
+ s.licenses = %w[Apache-2.0]
10
9
  s.platform = Gem::Platform::RUBY
11
- s.authors = [ 'Saku Ytti', 'Samer Abdel-Hafez', 'Anton Aksola' ]
12
- s.email = %w( saku@ytti.fi sam@arahant.net aakso@iki.fi)
10
+ s.authors = ['Saku Ytti', 'Samer Abdel-Hafez', 'Anton Aksola']
11
+ s.email = %w[saku@ytti.fi sam@arahant.net aakso@iki.fi]
13
12
  s.homepage = 'http://github.com/ytti/oxidized'
14
13
  s.summary = 'feeble attempt at rancid'
15
14
  s.description = 'software to fetch configuration from network devices and store them'
16
15
  s.rubyforge_project = s.name
17
- s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
- s.executables = %w( oxidized )
16
+ s.files = %x(git ls-files -z).split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ s.executables = %w[oxidized]
19
18
  s.require_path = 'lib'
20
19
 
21
- s.required_ruby_version = '>= 2.0.0'
20
+ s.required_ruby_version = '>= 2.3'
22
21
  s.add_runtime_dependency 'asetus', '~> 0.1'
23
- s.add_runtime_dependency 'slop', '~> 3.5'
24
- s.add_runtime_dependency 'net-ssh', '~> 3.0.2'
25
- s.add_runtime_dependency 'rugged', '~> 0.21', '>= 0.21.4'
22
+ s.add_runtime_dependency 'bcrypt_pbkdf', '~> 1.0'
23
+ s.add_runtime_dependency 'ed25519', '~> 1.2'
24
+ s.add_runtime_dependency 'net-ssh', '~> 5'
25
+ s.add_runtime_dependency 'net-telnet', '~> 0.2'
26
+ s.add_runtime_dependency 'rugged', '~> 0.28.0'
27
+ s.add_runtime_dependency 'slop', '~> 4.6'
26
28
 
27
- if defined?(RUBY_VERSION) && RUBY_VERSION > '2.3'
28
- s.add_runtime_dependency 'net-telnet', '~> 0'
29
- end
30
-
31
- s.add_development_dependency 'pry', '~> 0'
32
- s.add_development_dependency 'bundler', '~> 1.10'
33
- s.add_development_dependency 'rake', '~> 10.0'
29
+ s.add_development_dependency 'bundler', '~> 2.0'
30
+ s.add_development_dependency 'codecov' if ENV['CI'] == 'true'
31
+ s.add_development_dependency 'git', '~> 1'
34
32
  s.add_development_dependency 'minitest', '~> 5.8'
35
33
  s.add_development_dependency 'mocha', '~> 1.1'
36
- s.add_development_dependency 'git', '~> 1'
34
+ s.add_development_dependency 'pry', '~> 0'
35
+ s.add_development_dependency 'rake', '~> 10.0'
36
+ s.add_development_dependency 'rubocop', '~> 0.80.0'
37
+ s.add_development_dependency 'simplecov'
37
38
  end