oxidized 0.30.1 → 0.32.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +3 -4
- data/.github/workflows/stale.yml +4 -2
- data/.rubocop.yml +18 -3
- data/.rubocop_todo.yml +4 -11
- data/CHANGELOG.md +93 -1
- data/CONTRIBUTING.md +5 -0
- data/Dockerfile +84 -20
- data/README.md +5 -21
- data/Rakefile +31 -2
- data/docs/Configuration.md +50 -14
- data/docs/Creating-Models.md +75 -4
- data/docs/DeviceSimulation.md +184 -0
- data/docs/Hooks.md +39 -5
- data/docs/Issues.md +97 -0
- data/docs/Model-Notes/APC_AOS.md +29 -16
- data/docs/Model-Notes/Cumulus.md +5 -0
- data/docs/Model-Notes/FSOS.md +6 -0
- data/docs/Model-Notes/FortiOS.md +21 -5
- data/docs/Model-Notes/HPEAruba.md +31 -0
- data/docs/Model-Notes/OS6.md +10 -0
- data/docs/Model-Notes/RouterOS.md +15 -0
- data/docs/Model-Notes/SikluMHTG.md +7 -0
- data/docs/ModelUnitTests.md +186 -0
- data/docs/Outputs.md +2 -0
- data/docs/Release.md +18 -15
- data/docs/Sources.md +21 -0
- data/docs/Supported-OS-Types.md +14 -7
- data/docs/Troubleshooting.md +35 -0
- data/examples/podman-compose/Makefile +59 -17
- data/examples/podman-compose/README.md +63 -27
- data/examples/podman-compose/docker-compose.yml +11 -2
- data/examples/podman-compose/gitserver/.gitignore +1 -0
- data/examples/podman-compose/gitserver/Dockerfile +14 -0
- data/examples/podman-compose/model-simulation/Dockerfile-model +1 -1
- data/examples/podman-compose/model-simulation/asternos.sh +2 -0
- data/examples/podman-compose/oxidized-config/.gitignore +2 -0
- data/examples/podman-compose/oxidized-config/config +1 -1
- data/examples/podman-compose/oxidized-config/config_csv-file +46 -0
- data/examples/podman-compose/oxidized-config/config_csv-gitserver +56 -0
- data/examples/podman-compose/oxidized-ssh/.gitignore +1 -0
- data/extra/device2yaml.rb +245 -0
- data/extra/gitdiff-msteams.sh +32 -5
- data/extra/nagios_check_failing_nodes.rb +1 -1
- data/extra/rest_client.rb +1 -1
- data/lib/oxidized/config.rb +8 -2
- data/lib/oxidized/hook/githubrepo.rb +37 -7
- data/lib/oxidized/hook/slackdiff.rb +29 -7
- data/lib/oxidized/input/http.rb +1 -0
- data/lib/oxidized/input/ssh.rb +13 -5
- data/lib/oxidized/input/telnet.rb +1 -1
- data/lib/oxidized/manager.rb +17 -16
- data/lib/oxidized/model/aos7.rb +2 -0
- data/lib/oxidized/model/aoscx.rb +16 -2
- data/lib/oxidized/model/aosw.rb +8 -2
- data/lib/oxidized/model/apc_aos.rb +1 -1
- data/lib/oxidized/model/arubainstant.rb +90 -0
- data/lib/oxidized/model/asa.rb +2 -1
- data/lib/oxidized/model/asyncos.rb +1 -1
- data/lib/oxidized/model/audiocodes.rb +2 -2
- data/lib/oxidized/model/cnos.rb +13 -10
- data/lib/oxidized/model/cumulus.rb +19 -2
- data/lib/oxidized/model/dlink.rb +1 -0
- data/lib/oxidized/model/dlinknextgen.rb +3 -0
- data/lib/oxidized/model/edgecos.rb +2 -1
- data/lib/oxidized/model/enterprise_sonic.rb +46 -0
- data/lib/oxidized/model/eos.rb +2 -0
- data/lib/oxidized/model/f5os.rb +17 -0
- data/lib/oxidized/model/firewareos.rb +10 -1
- data/lib/oxidized/model/fortios.rb +24 -1
- data/lib/oxidized/model/fsos.rb +5 -1
- data/lib/oxidized/model/garderos.rb +43 -0
- data/lib/oxidized/model/h3c.rb +1 -1
- data/lib/oxidized/model/ibos.rb +1 -0
- data/lib/oxidized/model/ios.rb +20 -12
- data/lib/oxidized/model/iosxr.rb +1 -1
- data/lib/oxidized/model/junos.rb +1 -1
- data/lib/oxidized/model/kornfeldos.rb +33 -0
- data/lib/oxidized/model/lenovonos.rb +2 -0
- data/lib/oxidized/model/linuxgeneric.rb +1 -1
- data/lib/oxidized/model/model.rb +2 -2
- data/lib/oxidized/model/netgear.rb +1 -1
- data/lib/oxidized/model/nodegrid.rb +1 -1
- data/lib/oxidized/model/nsxdfw.rb +30 -0
- data/lib/oxidized/model/nxos.rb +2 -1
- data/lib/oxidized/model/os6.rb +48 -0
- data/lib/oxidized/model/rgos.rb +1 -1
- data/lib/oxidized/model/riverbed.rb +104 -0
- data/lib/oxidized/model/routeros.rb +2 -2
- data/lib/oxidized/model/saos.rb +18 -1
- data/lib/oxidized/model/siklumhtg.rb +22 -0
- data/lib/oxidized/model/sonicos.rb +8 -2
- data/lib/oxidized/model/tplink.rb +1 -0
- data/lib/oxidized/model/uplinkolt.rb +46 -0
- data/lib/oxidized/model/vyatta.rb +2 -2
- data/lib/oxidized/model/xos.rb +7 -0
- data/lib/oxidized/node.rb +30 -18
- data/lib/oxidized/nodes.rb +13 -5
- data/lib/oxidized/output/file.rb +45 -42
- data/lib/oxidized/output/git.rb +185 -160
- data/lib/oxidized/output/gitcrypt.rb +188 -186
- data/lib/oxidized/output/http.rb +53 -51
- data/lib/oxidized/output/output.rb +6 -4
- data/lib/oxidized/source/csv.rb +44 -49
- data/lib/oxidized/source/http.rb +63 -81
- data/lib/oxidized/source/jsonfile.rb +63 -0
- data/lib/oxidized/source/source.rb +73 -18
- data/lib/oxidized/source/sql.rb +66 -59
- data/lib/oxidized/version.rb +2 -2
- data/oxidized.gemspec +25 -18
- metadata +115 -21
data/lib/oxidized/output/git.rb
CHANGED
@@ -1,195 +1,220 @@
|
|
1
1
|
module Oxidized
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
class GitError < OxidizedError; end
|
6
|
-
begin
|
7
|
-
require 'rugged'
|
8
|
-
rescue LoadError
|
9
|
-
raise OxidizedError, 'rugged not found: sudo gem install rugged'
|
10
|
-
end
|
2
|
+
module Output
|
3
|
+
class Git < Output
|
4
|
+
using Refinements
|
11
5
|
|
12
|
-
|
6
|
+
class GitError < OxidizedError; end
|
7
|
+
begin
|
8
|
+
require 'rugged'
|
9
|
+
rescue LoadError
|
10
|
+
raise OxidizedError, 'rugged not found: sudo gem install rugged'
|
11
|
+
end
|
13
12
|
|
14
|
-
|
15
|
-
super
|
16
|
-
@cfg = Oxidized.config.output.git
|
17
|
-
end
|
13
|
+
attr_reader :commitref
|
18
14
|
|
19
|
-
|
20
|
-
|
21
|
-
Oxidized.
|
22
|
-
Oxidized.asetus.user.output.git.email = 'o@example.com'
|
23
|
-
Oxidized.asetus.user.output.git.repo = File.join(Config::ROOT, 'oxidized.git')
|
24
|
-
Oxidized.asetus.save :user
|
25
|
-
raise NoConfig, 'no output git config, edit ~/.config/oxidized/config'
|
15
|
+
def initialize
|
16
|
+
super
|
17
|
+
@cfg = Oxidized.config.output.git
|
26
18
|
end
|
27
19
|
|
28
|
-
|
29
|
-
@cfg.
|
30
|
-
|
20
|
+
def setup
|
21
|
+
if @cfg.empty?
|
22
|
+
Oxidized.asetus.user.output.git.user = 'Oxidized'
|
23
|
+
Oxidized.asetus.user.output.git.email = 'o@example.com'
|
24
|
+
Oxidized.asetus.user.output.git.repo = File.join(Config::ROOT, 'oxidized.git')
|
25
|
+
Oxidized.asetus.save :user
|
26
|
+
raise NoConfig, "no output git config, edit #{Oxidized::Config.configfile}"
|
31
27
|
end
|
32
|
-
else
|
33
|
-
@cfg.repo = File.expand_path @cfg.repo
|
34
|
-
end
|
35
|
-
end
|
36
28
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
@email = opt[:email] || @cfg.email
|
41
|
-
@opt = opt
|
42
|
-
@commitref = nil
|
43
|
-
repo = @cfg.repo
|
44
|
-
|
45
|
-
outputs.types.each do |type|
|
46
|
-
type_cfg = ''
|
47
|
-
type_repo = File.join(File.dirname(repo), type + '.git')
|
48
|
-
outputs.type(type).each do |output|
|
49
|
-
(type_cfg << output; next) unless output.name # rubocop:disable Style/Semicolon
|
50
|
-
type_file = file + '--' + output.name
|
51
|
-
if @cfg.type_as_directory?
|
52
|
-
type_file = type + '/' + type_file
|
53
|
-
type_repo = repo
|
29
|
+
if @cfg.repo.respond_to?(:each)
|
30
|
+
@cfg.repo.each do |group, repo|
|
31
|
+
@cfg.repo["#{group}="] = File.expand_path repo
|
54
32
|
end
|
55
|
-
|
33
|
+
else
|
34
|
+
@cfg.repo = File.expand_path @cfg.repo
|
56
35
|
end
|
57
|
-
update type_repo, file, type_cfg
|
58
36
|
end
|
59
37
|
|
60
|
-
|
61
|
-
|
38
|
+
def store(file, outputs, opt = {})
|
39
|
+
@msg = opt[:msg]
|
40
|
+
@user = opt[:user] || @cfg.user
|
41
|
+
@email = opt[:email] || @cfg.email
|
42
|
+
@opt = opt
|
43
|
+
@commitref = nil
|
44
|
+
repo = @cfg.repo
|
45
|
+
|
46
|
+
outputs.types.each do |type|
|
47
|
+
type_cfg = ''
|
48
|
+
type_repo = File.join(File.dirname(repo), type + '.git')
|
49
|
+
outputs.type(type).each do |output|
|
50
|
+
(type_cfg << output; next) unless output.name # rubocop:disable Style/Semicolon
|
51
|
+
type_file = file + '--' + output.name
|
52
|
+
if @cfg.type_as_directory?
|
53
|
+
type_file = type + '/' + type_file
|
54
|
+
type_repo = repo
|
55
|
+
end
|
56
|
+
update type_repo, type_file, output
|
57
|
+
end
|
58
|
+
update type_repo, file, type_cfg
|
59
|
+
end
|
62
60
|
|
63
|
-
|
64
|
-
|
65
|
-
repo = Rugged::Repository.new repo
|
66
|
-
index = repo.index
|
67
|
-
index.read_tree repo.head.target.tree unless repo.empty?
|
68
|
-
repo.read(index.get(path)[:oid]).data
|
69
|
-
rescue StandardError
|
70
|
-
'node not found'
|
71
|
-
end
|
61
|
+
update repo, file, outputs.to_cfg
|
62
|
+
end
|
72
63
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
repo
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
hash[:oid] = commit.oid
|
90
|
-
hash[:author] = commit.author
|
91
|
-
hash[:message] = commit.message
|
92
|
-
tab[i += 1] = hash
|
64
|
+
# Returns the configuration of group/node_name
|
65
|
+
#
|
66
|
+
# #fetch is called by Nodes#fetch
|
67
|
+
# Nodes#fetch creates a new Output object each time, so we cannot
|
68
|
+
# store the repo index in memory. But as we keep the repo index up
|
69
|
+
# to date on disk in #update_repo, we can read it from disk instead of
|
70
|
+
# rebuilding it each time.
|
71
|
+
def fetch(node, group)
|
72
|
+
repo, path = yield_repo_and_path(node, group)
|
73
|
+
repo = Rugged::Repository.new repo
|
74
|
+
# Read the index from disk
|
75
|
+
index = repo.index
|
76
|
+
|
77
|
+
repo.read(index.get(path)[:oid]).data
|
78
|
+
rescue StandardError
|
79
|
+
'node not found'
|
93
80
|
end
|
94
|
-
walker.reset
|
95
|
-
tab
|
96
|
-
rescue StandardError
|
97
|
-
'node not found'
|
98
|
-
end
|
99
81
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
repo = Rugged::Repository.new repo
|
104
|
-
repo.blob_at(oid, path).content
|
105
|
-
rescue StandardError
|
106
|
-
'version not found'
|
107
|
-
end
|
82
|
+
# give a hash of all oid revision for the given node, and the date of the commit
|
83
|
+
def version(node, group)
|
84
|
+
repo, path = yield_repo_and_path(node, group)
|
108
85
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
86
|
+
repo = Rugged::Repository.new repo
|
87
|
+
walker = Rugged::Walker.new(repo)
|
88
|
+
walker.sorting(Rugged::SORT_DATE)
|
89
|
+
walker.push(repo.head.target.oid)
|
90
|
+
i = -1
|
91
|
+
tab = []
|
92
|
+
walker.each do |commit|
|
93
|
+
# Diabled rubocop because the suggested .empty? does not work here.
|
94
|
+
next if commit.diff(paths: [path]).size.zero? # rubocop:disable Style/ZeroLengthPredicate
|
95
|
+
|
96
|
+
hash = {}
|
97
|
+
hash[:date] = commit.time.to_s
|
98
|
+
hash[:oid] = commit.oid
|
99
|
+
hash[:author] = commit.author
|
100
|
+
hash[:message] = commit.message
|
101
|
+
tab[i += 1] = hash
|
124
102
|
end
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
diff_commits = { patch: patch, stat: stat }
|
103
|
+
walker.reset
|
104
|
+
tab
|
105
|
+
rescue StandardError
|
106
|
+
'node not found'
|
130
107
|
end
|
131
108
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
109
|
+
# give the blob of a specific revision
|
110
|
+
def get_version(node, group, oid)
|
111
|
+
repo, path = yield_repo_and_path(node, group)
|
112
|
+
repo = Rugged::Repository.new repo
|
113
|
+
repo.blob_at(oid, path).content
|
114
|
+
rescue StandardError
|
115
|
+
'version not found'
|
116
|
+
end
|
136
117
|
|
137
|
-
|
118
|
+
# give a hash with the patch of a diff between 2 revision and the stats (added and deleted lines)
|
119
|
+
def get_diff(node, group, oid1, oid2)
|
120
|
+
diff_commits = nil
|
121
|
+
repo, = yield_repo_and_path(node, group)
|
122
|
+
repo = Rugged::Repository.new repo
|
123
|
+
commit = repo.lookup(oid1)
|
124
|
+
|
125
|
+
if oid2
|
126
|
+
commit_old = repo.lookup(oid2)
|
127
|
+
diff = repo.diff(commit_old, commit)
|
128
|
+
diff.each do |patch|
|
129
|
+
if /#{node.name}\s+/ =~ patch.to_s.lines.first
|
130
|
+
diff_commits = { patch: patch.to_s, stat: patch.stat }
|
131
|
+
break
|
132
|
+
end
|
133
|
+
end
|
134
|
+
else
|
135
|
+
stat = commit.parents[0].diff(commit).stat
|
136
|
+
stat = [stat[1], stat[2]]
|
137
|
+
patch = commit.parents[0].diff(commit).patch
|
138
|
+
diff_commits = { patch: patch, stat: stat }
|
139
|
+
end
|
138
140
|
|
139
|
-
|
140
|
-
|
141
|
+
diff_commits
|
142
|
+
rescue StandardError
|
143
|
+
'no diffs'
|
144
|
+
end
|
141
145
|
|
142
|
-
|
146
|
+
private
|
143
147
|
|
144
|
-
|
145
|
-
|
148
|
+
def yield_repo_and_path(node, group)
|
149
|
+
repo, path = node.repo, node.name
|
146
150
|
|
147
|
-
|
148
|
-
return if data.empty?
|
151
|
+
path = "#{group}/#{node.name}" if group && !group.empty? && @cfg.single_repo?
|
149
152
|
|
150
|
-
|
151
|
-
if @cfg.single_repo?
|
152
|
-
file = File.join @opt[:group], file
|
153
|
-
else
|
154
|
-
repo = if repo.is_a?(::String)
|
155
|
-
File.join File.dirname(repo), @opt[:group] + '.git'
|
156
|
-
else
|
157
|
-
repo[@opt[:group]]
|
158
|
-
end
|
159
|
-
end
|
153
|
+
[repo, path]
|
160
154
|
end
|
161
155
|
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
156
|
+
def update(repo, file, data)
|
157
|
+
return if data.empty?
|
158
|
+
|
159
|
+
if @opt[:group]
|
160
|
+
if @cfg.single_repo?
|
161
|
+
file = File.join @opt[:group], file
|
162
|
+
else
|
163
|
+
repo = if repo.is_a?(::String)
|
164
|
+
File.join File.dirname(repo), @opt[:group] + '.git'
|
165
|
+
else
|
166
|
+
repo[@opt[:group]]
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
166
171
|
begin
|
167
|
-
Rugged::Repository.
|
168
|
-
|
169
|
-
|
172
|
+
repo = Rugged::Repository.new repo
|
173
|
+
update_repo repo, file, data
|
174
|
+
rescue Rugged::OSError, Rugged::RepositoryError => e
|
175
|
+
begin
|
176
|
+
Rugged::Repository.init_at repo, :bare
|
177
|
+
rescue StandardError => create_error
|
178
|
+
raise GitError, "first '#{e.message}' was raised while opening git repo, then '#{create_error.message}' was while trying to create git repo"
|
179
|
+
end
|
180
|
+
retry
|
170
181
|
end
|
171
|
-
retry
|
172
182
|
end
|
173
|
-
end
|
174
|
-
|
175
|
-
def update_repo(repo, file, data)
|
176
|
-
oid_old = repo.blob_at(repo.head.target_id, file) rescue nil
|
177
|
-
return false if oid_old && (oid_old.content.b == data.b)
|
178
|
-
|
179
|
-
oid = repo.write data, :blob
|
180
|
-
index = repo.index
|
181
|
-
index.add path: file, oid: oid, mode: 0o100644
|
182
183
|
|
183
|
-
repo
|
184
|
-
|
185
|
-
@
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
184
|
+
# Uploads data into file in the repo
|
185
|
+
#
|
186
|
+
# @param [String] file: the file to save the configuration to
|
187
|
+
# @param [String] data: the configuration to save
|
188
|
+
# @param [Rugged::Repository] repo: the git repository to use
|
189
|
+
#
|
190
|
+
# If Oxidized.config.output.git.single_repo = false (which is the default),
|
191
|
+
# there will one repository for each group.
|
192
|
+
#
|
193
|
+
# update_repo caches the index on disk. An index is usually used in a
|
194
|
+
# working directory and not in a bare repository, which confuses users.
|
195
|
+
# The alternative would be to rebuild the index each time, which a little
|
196
|
+
# time consuming. Caching the index in memory is difficult because a new
|
197
|
+
# Output object is created each time #store is called.
|
198
|
+
def update_repo(repo, file, data)
|
199
|
+
oid_old = repo.blob_at(repo.head.target_id, file) rescue nil
|
200
|
+
return false if oid_old && (oid_old.content.b == data.b)
|
201
|
+
|
202
|
+
oid = repo.write data, :blob
|
203
|
+
# Read the index from disk
|
204
|
+
index = repo.index
|
205
|
+
index.add path: file, oid: oid, mode: 0o100644
|
206
|
+
|
207
|
+
repo.config['user.name'] = @user
|
208
|
+
repo.config['user.email'] = @email
|
209
|
+
@commitref = Rugged::Commit.create(repo,
|
210
|
+
tree: index.write_tree(repo),
|
211
|
+
message: @msg,
|
212
|
+
parents: repo.empty? ? [] : [repo.head.target].compact,
|
213
|
+
update_ref: 'HEAD')
|
214
|
+
|
215
|
+
index.write
|
216
|
+
true
|
217
|
+
end
|
193
218
|
end
|
194
219
|
end
|
195
220
|
end
|