oxidized 0.19.0 → 0.20.0

Sign up to get free protection for your applications and to get access to all the features.
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