oxidized 0.19.0 → 0.20.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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +17 -0
  3. data/Dockerfile +17 -2
  4. data/Gemfile +0 -1
  5. data/Gemfile.lock +10 -9
  6. data/README.md +205 -10
  7. data/extra/auto-reload-config.runit +1 -1
  8. data/extra/oxidized.init +3 -3
  9. data/extra/update-ca-certificates.runit +7 -0
  10. data/lib/oxidized/config.rb +4 -2
  11. data/lib/oxidized/config/vars.rb +5 -1
  12. data/lib/oxidized/hook/exec.rb +1 -0
  13. data/lib/oxidized/hook/slackdiff.rb +34 -0
  14. data/lib/oxidized/input/ssh.rb +4 -1
  15. data/lib/oxidized/model/aireos.rb +1 -1
  16. data/lib/oxidized/model/airos.rb +9 -4
  17. data/lib/oxidized/model/alvarion.rb +3 -1
  18. data/lib/oxidized/model/aosw.rb +22 -6
  19. data/lib/oxidized/model/asa.rb +3 -2
  20. data/lib/oxidized/model/cisconga.rb +19 -0
  21. data/lib/oxidized/model/comware.rb +6 -0
  22. data/lib/oxidized/model/cumulus.rb +15 -3
  23. data/lib/oxidized/model/fabricos.rb +2 -1
  24. data/lib/oxidized/model/fiberdriver.rb +4 -0
  25. data/lib/oxidized/model/firewareos.rb +7 -1
  26. data/lib/oxidized/model/fortios.rb +15 -4
  27. data/lib/oxidized/model/ios.rb +83 -8
  28. data/lib/oxidized/model/ironware.rb +5 -6
  29. data/lib/oxidized/model/junos.rb +3 -0
  30. data/lib/oxidized/model/mlnxos.rb +5 -1
  31. data/lib/oxidized/model/netgear.rb +32 -0
  32. data/lib/oxidized/model/nxos.rb +14 -1
  33. data/lib/oxidized/model/oneos.rb +58 -0
  34. data/lib/oxidized/model/opengear.rb +2 -0
  35. data/lib/oxidized/model/pfsense.rb +3 -2
  36. data/lib/oxidized/model/powerconnect.rb +1 -0
  37. data/lib/oxidized/model/procurve.rb +8 -3
  38. data/lib/oxidized/model/quantaos.rb +1 -1
  39. data/lib/oxidized/model/routeros.rb +8 -0
  40. data/lib/oxidized/model/saos.rb +3 -1
  41. data/lib/oxidized/model/siklu.rb +19 -0
  42. data/lib/oxidized/model/timos.rb +16 -0
  43. data/lib/oxidized/model/tplink.rb +65 -0
  44. data/lib/oxidized/model/voltaire.rb +56 -0
  45. data/lib/oxidized/model/voss.rb +33 -0
  46. data/lib/oxidized/model/zhoneolt.rb +52 -0
  47. data/lib/oxidized/node.rb +39 -10
  48. data/lib/oxidized/nodes.rb +2 -1
  49. data/lib/oxidized/output/gitcrypt.rb +244 -0
  50. data/lib/oxidized/source/csv.rb +10 -1
  51. data/lib/oxidized/source/http.rb +24 -7
  52. data/lib/oxidized/version.rb +1 -1
  53. data/lib/oxidized/worker.rb +3 -2
  54. data/oxidized.gemspec +2 -1
  55. metadata +29 -4
@@ -0,0 +1,56 @@
1
+ class VOLTAIRE < Oxidized::Model
2
+
3
+ prompt /([\w.@()-\[:\s\]]+[#>]\s|(One or more tests have failed.*))$/
4
+ comment '## '
5
+
6
+ # Pager Handling
7
+ expect /.+lines\s\d+\-\d+([\s]|\/\d+\s\(END\)\s).+$/ do |data, re|
8
+ send ' '
9
+ data.sub re, ''
10
+ end
11
+
12
+
13
+ cmd :all do |cfg|
14
+ cfg.gsub! /\[\?1h=\r/, '' # Pager Handling
15
+ cfg.gsub! /\r\[K/,'' # Pager Handling
16
+ cfg.gsub! /\s/, '' # Linebreak Handling
17
+ cfg.gsub! /^CPU\ load\ averages\:\s.+/, '' # Omit constantly changing CPU info
18
+ cfg.gsub! /^System\ memory\:\s.+/, '' # Omit constantly changing memory info
19
+ cfg.gsub! /^Uptime\:\s.+/, '' # Omit constantly changing uptime info
20
+ cfg.gsub! /.+Generated\ at\s\d+.+/, '' # Omit constantly changing generation time info
21
+ cfg = cfg.lines.to_a[2..-3].join
22
+ end
23
+
24
+ cmd :secret do |cfg|
25
+ cfg.gsub! /(snmp-server community).*/, ' <snmp-server community configuration removed>'
26
+ cfg.gsub! /username (\S+) password (\d+) (\S+).*/, '<secret hidden>'
27
+ cfg
28
+ end
29
+
30
+
31
+ cmd 'version show' do |cfg|
32
+ comment cfg
33
+ end
34
+
35
+ cmd 'firmware-version show' do |cfg|
36
+ comment cfg
37
+ end
38
+
39
+ cmd 'remote show' do |cfg|
40
+ cfg
41
+ end
42
+
43
+ cmd 'sm-info show' do |cfg|
44
+ cfg
45
+ end
46
+
47
+ cmd ' show' do |cfg|
48
+ cfg
49
+ end
50
+
51
+ cfg :ssh do
52
+ post_login "no\n"
53
+ password /^Password:\s*/
54
+ pre_logout 'exit'
55
+ end
56
+ end
@@ -0,0 +1,33 @@
1
+ class Voss < Oxidized::Model
2
+ # Avaya VSP Operating System Software(VOSS)
3
+ # Created by danielcoxman@gmail.com
4
+ # May 9, 2017
5
+ # This was tested on vsp4k and vsp8k
6
+
7
+ comment '# '
8
+
9
+ prompt /^[^\s#>]+[#>]$/
10
+
11
+ cmd 'show sys-info' do |cfg|
12
+ comment cfg
13
+ end
14
+
15
+ # more the config rather than doing a show run
16
+ cmd 'more config.cfg' do |cfg|
17
+ cfg
18
+ cfg.gsub! /^[^\s#>]+[#>]$/, ''
19
+ cfg.gsub! /^more config.cfg/, ''
20
+ end
21
+
22
+ cfg :telnet do
23
+ username /Login: $/
24
+ password /Password: $/
25
+ end
26
+
27
+ cfg :telnet, :ssh do
28
+ pre_logout 'exit'
29
+ post_login 'enable'
30
+ post_login 'terminal more disable'
31
+ end
32
+
33
+ end
@@ -0,0 +1,52 @@
1
+ class ZhoneOLT < Oxidized::Model
2
+ # Zhone OLT/MetroE/DSL devices (ONT uses a completely different CLI)
3
+
4
+ # the prompt can be anything on zhone, but it defaults to 'zXX>' and we
5
+ # always use hostname>
6
+ prompt /^(\r*[\w.@():-]+[>]\s?)$/
7
+ comment '# '
8
+
9
+ cmd :secret do |cfg|
10
+ cfg.gsub! /^(set configsyncpasswd = ) \S+/, '\\1 <removed>'
11
+ cfg.gsub! /^(set user-pass = ) \S+/, '\\1 <removed>'
12
+ cfg.gsub! /^(set auth-key = ) \S+/, '\\1 <removed>'
13
+ cfg.gsub! /^(set priv-key = ) \S+/, '\\1 <removed>'
14
+ cfg.gsub! /^(set ftp-password = ) \S+/, '\\1 <removed>'
15
+ cfg.gsub! /^(set community-name = ) \S+/, '\\1 <removed>'
16
+ cfg.gsub! /^(set communityname = ) \S+/, '\\1 <removed>'
17
+ cfg
18
+ end
19
+
20
+ cmd :all do |cfg|
21
+ cfg.each_line.to_a[1..-2].map{|line|line.delete("\r").rstrip}.join("\n") + "\n"
22
+ end
23
+
24
+ cmd 'swversion' do |cfg|
25
+ comment cfg
26
+ end
27
+
28
+ cmd 'slots' do |cfg|
29
+ comment cfg
30
+ end
31
+
32
+ cmd 'eeshow card' do |cfg|
33
+ comment cfg
34
+ end
35
+
36
+ cmd 'ethrpshow' do |cfg|
37
+ cfg = cfg.each_line.select { |line| line.match /Vendor (Name|OUI|Part|Revision)|Serial Number|Manufacturing Date/ }.join
38
+ comment cfg
39
+ end
40
+
41
+ cmd 'dump console' do |cfg|
42
+ cfg = cfg.each_line.select { |line| not line.match /To Abort the operation enter Ctrl-C/ }.join
43
+ end
44
+
45
+ # zhone technically supports ssh, but it locks up a ton. Especially when
46
+ # showing large amounts of output, like "dump console"
47
+ cfg :telnet do
48
+ username /\r*login:/
49
+ password /\r*password:/
50
+ pre_logout 'logout'
51
+ end
52
+ end
@@ -10,8 +10,11 @@ module Oxidized
10
10
  alias :running? :running
11
11
  def initialize opt
12
12
  Oxidized.logger.debug 'resolving DNS for %s...' % opt[:name]
13
+ # remove the prefix if an IP Address is provided with one as IPAddr converts it to a network address.
14
+ ip_addr, _ = opt[:ip].to_s.split("/")
15
+ Oxidized.logger.debug 'IPADDR %s' % ip_addr.to_s
13
16
  @name = opt[:name]
14
- @ip = IPAddr.new(opt[:ip]).to_s rescue nil
17
+ @ip = IPAddr.new(ip_addr).to_s rescue nil
15
18
  @ip ||= Resolv.new.getaddress @name
16
19
  @group = opt[:group]
17
20
  @input = resolve_input opt
@@ -163,18 +166,32 @@ module Oxidized
163
166
  end
164
167
 
165
168
  def resolve_repo opt
166
- return unless is_git? opt
167
-
168
- remote_repo = Oxidized.config.output.git.repo
169
-
170
- if remote_repo.is_a?(::String)
171
- if Oxidized.config.output.git.single_repo? || @group.nil?
172
- remote_repo
169
+ if is_git? opt
170
+ remote_repo = Oxidized.config.output.git.repo
171
+
172
+ if remote_repo.is_a?(::String)
173
+ if Oxidized.config.output.git.single_repo? || @group.nil?
174
+ remote_repo
175
+ else
176
+ File.join(File.dirname(remote_repo), @group + '.git')
177
+ end
178
+ else
179
+ remote_repo[@group]
180
+ end
181
+ elsif is_gitcrypt? opt
182
+ remote_repo = Oxidized.config.output.gitcrypt.repo
183
+
184
+ if remote_repo.is_a?(::String)
185
+ if Oxidized.config.output.gitcrypt.single_repo? || @group.nil?
186
+ remote_repo
187
+ else
188
+ File.join(File.dirname(remote_repo), @group + '.git')
189
+ end
173
190
  else
174
- File.join(File.dirname(remote_repo), @group + '.git')
191
+ remote_repo[@group]
175
192
  end
176
193
  else
177
- remote_repo[@group]
194
+ return
178
195
  end
179
196
  end
180
197
 
@@ -199,6 +216,14 @@ module Oxidized
199
216
  end
200
217
  end
201
218
 
219
+ #model
220
+ if Oxidized.config.models.has_key?(@model.class.name.to_s.downcase)
221
+ if Oxidized.config.models[@model.class.name.to_s.downcase].has_key?(key_str)
222
+ value = Oxidized.config.models[@model.class.name.to_s.downcase][key_str]
223
+ Oxidized.logger.debug "node.rb: setting node key '#{key}' to value '#{value}' from model"
224
+ end
225
+ end
226
+
202
227
  #node
203
228
  value = opt[key_sym] || value
204
229
  Oxidized.logger.debug "node.rb: returning node key '#{key}' with value '#{value}'"
@@ -209,5 +234,9 @@ module Oxidized
209
234
  (opt[:output] || Oxidized.config.output.default) == 'git'
210
235
  end
211
236
 
237
+ def is_gitcrypt? opt
238
+ (opt[:output] || Oxidized.config.output.default) == 'gitcrypt'
239
+ end
240
+
212
241
  end
213
242
  end
@@ -4,7 +4,7 @@ module Oxidized
4
4
  class Oxidized::NotSupported < OxidizedError; end
5
5
  class Oxidized::NodeNotFound < OxidizedError; end
6
6
  class Nodes < Array
7
- attr_accessor :source
7
+ attr_accessor :source, :jobs
8
8
  alias :put :unshift
9
9
  def load node_want=nil
10
10
  with_lock do
@@ -73,6 +73,7 @@ module Oxidized
73
73
  # set last job to nil so that the node is picked for immediate update
74
74
  n.last = nil
75
75
  put n
76
+ jobs.want += 1 if Oxidized.config.next_adds_job?
76
77
  end
77
78
  end
78
79
  end
@@ -0,0 +1,244 @@
1
+ module Oxidized
2
+ class GitCrypt < Output
3
+ class GitCryptError < OxidizedError; end
4
+ begin
5
+ require 'git'
6
+ rescue LoadError
7
+ raise OxidizedError, 'git not found: sudo gem install ruby-git'
8
+ end
9
+
10
+ attr_reader :commitref
11
+
12
+ def initialize
13
+ @cfg = Oxidized.config.output.gitcrypt
14
+ @gitcrypt_cmd = "/usr/bin/git-crypt"
15
+ @gitcrypt_init = @gitcrypt_cmd + " init"
16
+ @gitcrypt_unlock = @gitcrypt_cmd + " unlock"
17
+ @gitcrypt_lock = @gitcrypt_cmd + " lock"
18
+ @gitcrypt_adduser = @gitcrypt_cmd + " add-gpg-user --trusted "
19
+ end
20
+
21
+ def setup
22
+ if @cfg.empty?
23
+ Oxidized.asetus.user.output.gitcrypt.user = 'Oxidized'
24
+ Oxidized.asetus.user.output.gitcrypt.email = 'o@example.com'
25
+ Oxidized.asetus.user.output.gitcrypt.repo = File.join(Config::Root, 'oxidized.git')
26
+ Oxidized.asetus.save :user
27
+ raise NoConfig, 'no output git config, edit ~/.config/oxidized/config'
28
+ end
29
+
30
+ if @cfg.repo.respond_to?(:each)
31
+ @cfg.repo.each do |group, repo|
32
+ @cfg.repo["#{group}="] = File.expand_path repo
33
+ end
34
+ else
35
+ @cfg.repo = File.expand_path @cfg.repo
36
+ end
37
+ end
38
+
39
+ def crypt_init repo
40
+ repo.chdir do
41
+ system(@gitcrypt_init)
42
+ @cfg.users.each do |user|
43
+ system("#{@gitcrypt_adduser} #{user}")
44
+ end
45
+ File.write(".gitattributes", "* filter=git-crypt diff=git-crypt\n.gitattributes !filter !diff")
46
+ repo.add(".gitattributes")
47
+ repo.commit("Initial commit: crypt all config files")
48
+ end
49
+ end
50
+
51
+ def lock repo
52
+ repo.chdir do
53
+ system(@gitcrypt_lock)
54
+ end
55
+ end
56
+
57
+ def unlock repo
58
+ repo.chdir do
59
+ system(@gitcrypt_unlock)
60
+ end
61
+ end
62
+
63
+ def store file, outputs, opt={}
64
+ @msg = opt[:msg]
65
+ @user = (opt[:user] or @cfg.user)
66
+ @email = (opt[:email] or @cfg.email)
67
+ @opt = opt
68
+ @commitref = nil
69
+ repo = @cfg.repo
70
+
71
+ outputs.types.each do |type|
72
+ type_cfg = ''
73
+ type_repo = File.join(File.dirname(repo), type + '.git')
74
+ outputs.type(type).each do |output|
75
+ (type_cfg << output; next) if not output.name
76
+ type_file = file + '--' + output.name
77
+ if @cfg.type_as_directory?
78
+ type_file = type + '/' + type_file
79
+ type_repo = repo
80
+ end
81
+ update type_repo, type_file, output
82
+ end
83
+ update type_repo, file, type_cfg
84
+ end
85
+
86
+ update repo, file, outputs.to_cfg
87
+ end
88
+
89
+
90
+ def fetch node, group
91
+ begin
92
+ repo, path = yield_repo_and_path(node, group)
93
+ repo = Git.open repo
94
+ unlock repo
95
+ index = repo.index
96
+ # Empty repo ?
97
+ empty = File.exists? index.path
98
+ if empty
99
+ raise 'Empty git repo'
100
+ else
101
+ File.read path
102
+ end
103
+ lock repo
104
+ rescue
105
+ 'node not found'
106
+ end
107
+ end
108
+
109
+ # give a hash of all oid revision for the given node, and the date of the commit
110
+ def version node, group
111
+ begin
112
+ repo, path = yield_repo_and_path(node, group)
113
+
114
+ repo = Git.open repo
115
+ unlock repo
116
+ walker = repo.log.path(path)
117
+ i = -1
118
+ tab = []
119
+ walker.each do |commit|
120
+ hash = {}
121
+ hash[:date] = commit.date.to_s
122
+ hash[:oid] = commit.objectish
123
+ hash[:author] = commit.author
124
+ hash[:message] = commit.message
125
+ tab[i += 1] = hash
126
+ end
127
+ walker.reset
128
+ tab
129
+ rescue
130
+ 'node not found'
131
+ end
132
+ end
133
+
134
+ #give the blob of a specific revision
135
+ def get_version node, group, oid
136
+ begin
137
+ repo, path = yield_repo_and_path(node, group)
138
+ repo = Git.open repo
139
+ unlock repo
140
+ repo.gtree(oid).files[path].contents
141
+ rescue
142
+ 'version not found'
143
+ ensure
144
+ lock repo
145
+ end
146
+ end
147
+
148
+ #give a hash with the patch of a diff between 2 revision and the stats (added and deleted lines)
149
+ def get_diff node, group, oid1, oid2
150
+ begin
151
+ diff_commits = nil
152
+ repo, path = yield_repo_and_path(node, group)
153
+ repo = Git.open repo
154
+ unlock repo
155
+ commit = repo.gcommit(oid1)
156
+
157
+ if oid2
158
+ commit_old = repo.gcommit(oid2)
159
+ diff = repo.diff(commit_old, commit)
160
+ stats = [diff.stats[:files][node.name][:insertions], diff.stats[:files][node.name][:deletions]]
161
+ diff.each do |patch|
162
+ if /#{node.name}\s+/.match(patch.patch.to_s.lines.first)
163
+ diff_commits = {:patch => patch.patch.to_s, :stat => stats}
164
+ break
165
+ end
166
+ end
167
+ else
168
+ stat = commit.parents[0].diff(commit).stats
169
+ stat = [stat[:files][node.name][:insertions],stat[:files][node.name][:deletions]]
170
+ patch = commit.parents[0].diff(commit).patch
171
+ diff_commits = {:patch => patch, :stat => stat}
172
+ end
173
+ lock repo
174
+ diff_commits
175
+ rescue
176
+ 'no diffs'
177
+ ensure
178
+ lock repo
179
+ end
180
+ end
181
+
182
+ private
183
+
184
+ def yield_repo_and_path(node, group)
185
+ repo, path = node.repo, node.name
186
+
187
+ if group and @cfg.single_repo?
188
+ path = "#{group}/#{node.name}"
189
+ end
190
+
191
+ [repo, path]
192
+ end
193
+
194
+ def update repo, file, data
195
+ return if data.empty?
196
+
197
+ if @opt[:group]
198
+ if @cfg.single_repo?
199
+ file = File.join @opt[:group], file
200
+ else
201
+ repo = if repo.is_a?(::String)
202
+ File.join File.dirname(repo), @opt[:group] + '.git'
203
+ else
204
+ repo[@opt[:group]]
205
+ end
206
+ end
207
+ end
208
+
209
+ begin
210
+ update_repo repo, file, data, @msg, @user, @email
211
+ rescue Git::GitExecuteError, ArgumentError => open_error
212
+ Oxidized.logger.debug "open_error #{open_error} #{file}"
213
+ begin
214
+ grepo = Git.init repo
215
+ crypt_init grepo
216
+ rescue => create_error
217
+ raise GitCryptError, "first '#{open_error.message}' was raised while opening git repo, then '#{create_error.message}' was while trying to create git repo"
218
+ end
219
+ retry
220
+ end
221
+ end
222
+
223
+ def update_repo repo, file, data, msg, user, email
224
+ grepo = Git.open repo
225
+ grepo.config('user.name', user)
226
+ grepo.config('user.email', email)
227
+ grepo.chdir do
228
+ unlock grepo
229
+ File.write(file, data)
230
+ grepo.add(file)
231
+ if grepo.status[file].nil?
232
+ grepo.commit(msg)
233
+ @commitref = grepo.log(1).first.objectish
234
+ true
235
+ elsif !grepo.status[file].type.nil?
236
+ grepo.commit(msg)
237
+ @commitref = grepo.log(1).first.objectish
238
+ true
239
+ end
240
+ lock grepo
241
+ end
242
+ end
243
+ end
244
+ end