oxidized 0.29.1 → 0.37.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.
- checksums.yaml +4 -4
- data/.coderabbit.yaml +21 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +45 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +22 -0
- data/.github/ISSUE_TEMPLATE/support-request.md +39 -0
- data/.github/workflows/codeql.yml +4 -4
- data/.github/workflows/publishdocker.yml +108 -13
- data/.github/workflows/ruby.yml +3 -12
- data/.github/workflows/stale.yml +6 -1
- data/.gitignore +2 -0
- data/.rubocop.yml +69 -13
- data/.rubocop_todo.yml +13 -143
- data/CHANGELOG.md +431 -4
- data/CONTRIBUTING.md +189 -0
- data/Dockerfile +89 -34
- data/README.md +59 -188
- data/Rakefile +58 -12
- data/bin/oxidized +2 -5
- data/docs/Configuration.md +382 -149
- data/docs/Creating-Models.md +262 -10
- data/docs/DeviceSimulation.md +196 -0
- data/docs/Docker.md +246 -0
- data/docs/Hooks.md +146 -51
- data/docs/Inputs.md +237 -0
- data/docs/Issues.md +123 -0
- data/docs/Model-Notes/APC.md +72 -0
- data/docs/Model-Notes/Cumulus.md +5 -0
- data/docs/Model-Notes/EatonNetwork.md +18 -0
- data/docs/Model-Notes/ExaLink.md +43 -0
- data/docs/Model-Notes/FSOS.md +17 -0
- data/docs/Model-Notes/Fortinet.md +75 -0
- data/docs/Model-Notes/GrandstreamHT8xx.md +8 -0
- data/docs/Model-Notes/HPEAruba.md +32 -0
- data/docs/Model-Notes/IvantiConnectSecure.md +59 -0
- data/docs/Model-Notes/OS6.md +10 -0
- data/docs/Model-Notes/README.md +1 -20
- data/docs/Model-Notes/RouterOS.md +28 -0
- data/docs/Model-Notes/SikluMHTG.md +7 -0
- data/docs/Model-Notes/TrueNAS.md +23 -0
- data/docs/Model-Notes/XGS4600-Zyxel.md +5 -0
- data/docs/ModelUnitTests.md +219 -0
- data/docs/Outputs.md +102 -5
- data/docs/Release.md +102 -0
- data/docs/Ruby-API.md +135 -2
- data/docs/Sources.md +34 -0
- data/docs/Supported-OS-Types.md +207 -270
- data/docs/Troubleshooting.md +43 -1
- data/extra/device2yaml.rb +259 -0
- data/extra/gitdiff-msteams.sh +32 -5
- data/extra/hooks/modelrules.rb +55 -0
- data/extra/hooks/modelrulesadvanced.rb +167 -0
- data/extra/hooks/srcipmap.rb +54 -0
- data/extra/nagios_check_failing_nodes.rb +1 -1
- data/extra/oxidized.runit +1 -1
- data/extra/rest_client.rb +5 -4
- data/extra/syslog.rb +11 -6
- data/lib/oxidized/cli/support.rb +152 -0
- data/lib/oxidized/cli.rb +21 -6
- data/lib/oxidized/config/vars.rb +33 -14
- data/lib/oxidized/config.rb +28 -17
- data/lib/oxidized/core.rb +50 -8
- data/lib/oxidized/hook/ciscosparkdiff.rb +11 -9
- data/lib/oxidized/hook/exec.rb +5 -4
- data/lib/oxidized/hook/githubrepo.rb +55 -18
- data/lib/oxidized/hook/noophook.rb +2 -2
- data/lib/oxidized/hook/slackdiff.rb +50 -14
- data/lib/oxidized/hook/xmppdiff.rb +9 -9
- data/lib/oxidized/hook.rb +68 -16
- data/lib/oxidized/input/cli.rb +8 -3
- data/lib/oxidized/input/debugtext.rb +40 -0
- data/lib/oxidized/input/debugyaml.rb +82 -0
- data/lib/oxidized/input/exec.rb +2 -11
- data/lib/oxidized/input/ftp.rb +2 -19
- data/lib/oxidized/input/http.rb +67 -17
- data/lib/oxidized/input/input.rb +35 -14
- data/lib/oxidized/input/scp.rb +27 -0
- data/lib/oxidized/input/ssh.rb +59 -83
- data/lib/oxidized/input/sshbase.rb +102 -0
- data/lib/oxidized/input/telnet.rb +20 -19
- data/lib/oxidized/input/tftp.rb +8 -8
- data/lib/oxidized/job.rb +18 -7
- data/lib/oxidized/jobs.rb +11 -1
- data/lib/oxidized/logger.rb +51 -0
- data/lib/oxidized/manager.rb +18 -17
- data/lib/oxidized/model/acos.rb +2 -1
- data/lib/oxidized/model/addpack.rb +26 -0
- data/lib/oxidized/model/adtran.rb +5 -1
- data/lib/oxidized/model/adva.rb +3 -3
- data/lib/oxidized/model/airfiber.rb +1 -1
- data/lib/oxidized/model/aos7.rb +11 -0
- data/lib/oxidized/model/aoscx.rb +37 -29
- data/lib/oxidized/model/aosw.rb +39 -29
- data/lib/oxidized/model/apc_aos.rb +7 -2
- data/lib/oxidized/model/apcaos.rb +39 -0
- data/lib/oxidized/model/aricentiss.rb +9 -2
- data/lib/oxidized/model/arubainstant.rb +93 -0
- data/lib/oxidized/model/asa.rb +11 -10
- data/lib/oxidized/model/asternos.rb +22 -0
- data/lib/oxidized/model/asyncos.rb +3 -3
- data/lib/oxidized/model/audiocodes.rb +2 -2
- data/lib/oxidized/model/awplus.rb +15 -12
- data/lib/oxidized/model/axos.rb +1 -1
- data/lib/oxidized/model/bdcom.rb +1 -0
- data/lib/oxidized/model/c4cmts.rb +1 -2
- data/lib/oxidized/model/ciscosma.rb +1 -1
- data/lib/oxidized/model/ciscosmb.rb +6 -1
- data/lib/oxidized/model/cnos.rb +13 -10
- data/lib/oxidized/model/comtrol.rb +1 -1
- data/lib/oxidized/model/comware.rb +5 -3
- data/lib/oxidized/model/cumulus.rb +14 -2
- data/lib/oxidized/model/defacto.rb +26 -0
- data/lib/oxidized/model/dellx.rb +1 -1
- data/lib/oxidized/model/dlink.rb +5 -2
- data/lib/oxidized/model/dlinknextgen.rb +55 -0
- data/lib/oxidized/model/dnos.rb +3 -0
- data/lib/oxidized/model/dslcommands.rb +93 -0
- data/lib/oxidized/model/dslsetup.rb +102 -0
- data/lib/oxidized/model/eatonnetwork.rb +70 -0
- data/lib/oxidized/model/edgecos.rb +3 -1
- data/lib/oxidized/model/edgeos.rb +7 -6
- data/lib/oxidized/model/edgeswitch.rb +3 -1
- data/lib/oxidized/model/efos.rb +41 -0
- data/lib/oxidized/model/eltex.rb +4 -2
- data/lib/oxidized/model/enterasys800.rb +2 -2
- data/lib/oxidized/model/enterprise_sonic.rb +46 -0
- data/lib/oxidized/model/eos.rb +3 -1
- data/lib/oxidized/model/exalink.rb +36 -0
- data/lib/oxidized/model/f5os.rb +17 -0
- data/lib/oxidized/model/fabricos.rb +2 -2
- data/lib/oxidized/model/fastiron.rb +5 -3
- data/lib/oxidized/model/firebrick.rb +2 -2
- data/lib/oxidized/model/firelinuxos.rb +13 -6
- data/lib/oxidized/model/firewareos.rb +10 -1
- data/lib/oxidized/model/fortigate.rb +160 -0
- data/lib/oxidized/model/fortios.rb +38 -48
- data/lib/oxidized/model/fsos.rb +46 -0
- data/lib/oxidized/model/gaiaos.rb +4 -4
- data/lib/oxidized/model/garderos.rb +38 -0
- data/lib/oxidized/model/grandstreamht8xx.rb +19 -0
- data/lib/oxidized/model/h3c.rb +1 -1
- data/lib/oxidized/model/ibos.rb +1 -0
- data/lib/oxidized/model/ingate.rb +47 -0
- data/lib/oxidized/model/ios.rb +57 -30
- data/lib/oxidized/model/iosxr.rb +2 -2
- data/lib/oxidized/model/ironware.rb +7 -5
- data/lib/oxidized/model/ivanti.rb +54 -0
- data/lib/oxidized/model/junos.rb +12 -5
- data/lib/oxidized/model/kornfeldos.rb +33 -0
- data/lib/oxidized/model/lenovonos.rb +2 -0
- data/lib/oxidized/model/linksyssrw.rb +3 -3
- data/lib/oxidized/model/linuxgeneric.rb +4 -2
- data/lib/oxidized/model/macros.rb +60 -0
- data/lib/oxidized/model/mimosab11.rb +34 -0
- data/lib/oxidized/model/ml66.rb +33 -0
- data/lib/oxidized/model/mlnxos.rb +18 -14
- data/lib/oxidized/model/model.rb +98 -103
- data/lib/oxidized/model/mtrlrfs.rb +1 -0
- data/lib/oxidized/model/ndms.rb +6 -0
- data/lib/oxidized/model/netgear.rb +14 -3
- data/lib/oxidized/model/netonix.rb +1 -1
- data/lib/oxidized/model/netscaler.rb +1 -1
- data/lib/oxidized/model/nodegrid.rb +1 -1
- data/lib/oxidized/model/nsxdfw.rb +31 -0
- data/lib/oxidized/model/nsxfirewall.rb +2 -1
- data/lib/oxidized/model/nxos.rb +20 -15
- data/lib/oxidized/model/ocnos.rb +42 -0
- data/lib/oxidized/model/onefinity.rb +18 -0
- data/lib/oxidized/model/openbsd.rb +1 -1
- data/lib/oxidized/model/opengear.rb +36 -1
- data/lib/oxidized/model/openwrt.rb +6 -6
- data/lib/oxidized/model/opnsense.rb +9 -1
- data/lib/oxidized/model/os6.rb +48 -0
- data/lib/oxidized/model/outputs.rb +5 -0
- data/lib/oxidized/model/panos.rb +2 -0
- data/lib/oxidized/model/perle.rb +41 -0
- data/lib/oxidized/model/pfsense.rb +9 -0
- data/lib/oxidized/model/powerconnect.rb +32 -11
- data/lib/oxidized/model/procurve.rb +6 -2
- data/lib/oxidized/model/qtech.rb +3 -1
- data/lib/oxidized/model/quantaos.rb +8 -6
- data/lib/oxidized/model/rgos.rb +33 -0
- data/lib/oxidized/model/riverbed.rb +104 -0
- data/lib/oxidized/model/routeros.rb +18 -11
- data/lib/oxidized/model/saos.rb +18 -1
- data/lib/oxidized/model/saos10.rb +38 -0
- data/lib/oxidized/model/siklu.rb +1 -1
- data/lib/oxidized/model/siklumhtg.rb +22 -0
- data/lib/oxidized/model/sixwind.rb +28 -0
- data/lib/oxidized/model/slxos.rb +3 -3
- data/lib/oxidized/model/smartbyte.rb +48 -0
- data/lib/oxidized/model/sonicos.rb +27 -20
- data/lib/oxidized/model/speedtouch.rb +1 -1
- data/lib/oxidized/model/sros.rb +3 -3
- data/lib/oxidized/model/srosmd.rb +1 -1
- data/lib/oxidized/model/supermicro.rb +1 -1
- data/lib/oxidized/model/telco.rb +1 -1
- data/lib/oxidized/model/tmos.rb +1 -0
- data/lib/oxidized/model/tnsr.rb +57 -0
- data/lib/oxidized/model/tplink.rb +5 -5
- data/lib/oxidized/model/trango.rb +4 -2
- data/lib/oxidized/model/truenas.rb +64 -3
- data/lib/oxidized/model/ucs.rb +1 -1
- data/lib/oxidized/model/unifiap.rb +144 -0
- data/lib/oxidized/model/uplinkolt.rb +46 -0
- data/lib/oxidized/model/voltaire.rb +1 -1
- data/lib/oxidized/model/voss.rb +3 -0
- data/lib/oxidized/model/vrp.rb +5 -1
- data/lib/oxidized/model/vyatta.rb +9 -3
- data/lib/oxidized/model/vyos.rb +37 -0
- data/lib/oxidized/model/xos.rb +10 -1
- data/lib/oxidized/model/zhoneolt.rb +4 -2
- data/lib/oxidized/model/zynos.rb +67 -3
- data/lib/oxidized/model/zynosadsl.rb +14 -0
- data/lib/oxidized/model/zynoscli.rb +1 -1
- data/lib/oxidized/model/zynosgs.rb +2 -0
- data/lib/oxidized/model/zynosmgs.rb +32 -0
- data/lib/oxidized/node.rb +99 -64
- data/lib/oxidized/nodes.rb +40 -19
- data/lib/oxidized/output/file.rb +80 -43
- data/lib/oxidized/output/git.rb +295 -151
- data/lib/oxidized/output/gitcrypt.rb +193 -185
- data/lib/oxidized/output/http.rb +63 -51
- data/lib/oxidized/output/output.rb +20 -4
- data/lib/oxidized/signals.rb +44 -0
- data/lib/oxidized/source/csv.rb +49 -49
- data/lib/oxidized/source/http.rb +76 -63
- data/lib/oxidized/source/jsonfile.rb +68 -0
- data/lib/oxidized/source/source.rb +73 -18
- data/lib/oxidized/source/sql.rb +71 -59
- data/lib/oxidized/version.rb +6 -4
- data/lib/oxidized/worker.rb +57 -29
- data/lib/oxidized.rb +4 -24
- data/lib/refinements.rb +21 -1
- data/oxidized.gemspec +45 -21
- metadata +248 -41
- data/lib/oxidized/model/timos.rb +0 -10
data/lib/oxidized/output/file.rb
CHANGED
|
@@ -1,55 +1,92 @@
|
|
|
1
1
|
module Oxidized
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
module Output
|
|
3
|
+
# ruby's File class must be accessed with ::File to avoid name conflicts
|
|
4
|
+
class File < Output
|
|
5
|
+
require 'fileutils'
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
attr_reader :commitref
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
def initialize
|
|
10
|
+
super
|
|
11
|
+
@cfg = Oxidized.config.output.file
|
|
12
|
+
end
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
def setup
|
|
15
|
+
return unless @cfg.empty?
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
Oxidized.asetus.user.output.file.directory = ::File.join(Config::ROOT, 'configs')
|
|
18
|
+
Oxidized.asetus.save :user
|
|
19
|
+
raise NoConfig, "no output file config, edit #{Oxidized::Config.configfile}"
|
|
20
|
+
end
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
# node: node name (String)
|
|
23
|
+
# outputs: Oxidized::Models::Outputs
|
|
24
|
+
# opts: dict of optional parameters:
|
|
25
|
+
# - group: node group
|
|
26
|
+
# - significant_changes:
|
|
27
|
+
# nil / not set / true -> store as usual
|
|
28
|
+
# false -> do not store
|
|
29
|
+
def store(node, outputs, opt = {})
|
|
30
|
+
return false if opt[:significant_changes] == false
|
|
28
31
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
elsif File.exist? File.join(cfg_dir, node_name) # node configuration file is stored on base directory
|
|
37
|
-
File.read File.join(cfg_dir, node_name)
|
|
38
|
-
else
|
|
39
|
-
path = Dir.glob(File.join(File.dirname(cfg_dir), '**', node_name)).first # fetch node in all groups
|
|
40
|
-
File.read path
|
|
41
|
-
end
|
|
42
|
-
rescue Errno::ENOENT
|
|
43
|
-
nil
|
|
44
|
-
end
|
|
32
|
+
file = ::File.expand_path @cfg.directory
|
|
33
|
+
file = ::File.join ::File.dirname(file), opt[:group] if opt[:group]
|
|
34
|
+
FileUtils.mkdir_p file
|
|
35
|
+
file = ::File.join file, node
|
|
36
|
+
::File.write(file, outputs.to_cfg)
|
|
37
|
+
@commitref = file
|
|
38
|
+
end
|
|
45
39
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
40
|
+
def fetch(node, group)
|
|
41
|
+
cfg_dir = ::File.expand_path @cfg.directory
|
|
42
|
+
node_name = node.name
|
|
43
|
+
|
|
44
|
+
if group # group is explicitly defined by user
|
|
45
|
+
cfg_dir = ::File.join ::File.dirname(cfg_dir), group
|
|
46
|
+
::File.read ::File.join(cfg_dir, node_name)
|
|
47
|
+
elsif ::File.exist? ::File.join(cfg_dir, node_name) # node configuration file is stored on base directory
|
|
48
|
+
::File.read ::File.join(cfg_dir, node_name)
|
|
49
|
+
else
|
|
50
|
+
path = Dir.glob(::File.join(::File.dirname(cfg_dir), '**', node_name)).first # fetch node in all groups
|
|
51
|
+
::File.read path
|
|
52
|
+
end
|
|
53
|
+
rescue Errno::ENOENT
|
|
54
|
+
nil
|
|
55
|
+
end
|
|
50
56
|
|
|
51
|
-
|
|
52
|
-
|
|
57
|
+
def version(_node, _group)
|
|
58
|
+
# not supported
|
|
59
|
+
[]
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def get_version(_node, _group, _oid)
|
|
63
|
+
'not supported'
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def self.node_path(node_name, group_name = nil)
|
|
67
|
+
cfg_dir = ::File.expand_path Oxidized.config.output.file.directory
|
|
68
|
+
|
|
69
|
+
if group_name
|
|
70
|
+
::File.join ::File.dirname(cfg_dir), group_name, node_name
|
|
71
|
+
else
|
|
72
|
+
::File.join cfg_dir, node_name
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def self.clean_obsolete_nodes(active_nodes)
|
|
77
|
+
cfg_dir = ::File.expand_path Oxidized.config.output.file.directory
|
|
78
|
+
dir_base = ::File.dirname(cfg_dir)
|
|
79
|
+
default_dir = ::File.basename(cfg_dir)
|
|
80
|
+
|
|
81
|
+
keep_files = active_nodes.map { |n| node_path(n.name, n.group) }
|
|
82
|
+
active_groups = active_nodes.map(&:group).compact.uniq
|
|
83
|
+
|
|
84
|
+
[default_dir, *active_groups].each do |group|
|
|
85
|
+
Dir.glob(::File.join(dir_base, group, "*")).each do |file|
|
|
86
|
+
::File.delete(file) if ::File.file?(file) && !keep_files.include?(file)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
53
90
|
end
|
|
54
91
|
end
|
|
55
92
|
end
|
data/lib/oxidized/output/git.rb
CHANGED
|
@@ -1,193 +1,337 @@
|
|
|
1
1
|
module Oxidized
|
|
2
|
-
|
|
3
|
-
class
|
|
4
|
-
|
|
5
|
-
require 'rugged'
|
|
6
|
-
rescue LoadError
|
|
7
|
-
raise OxidizedError, 'rugged not found: sudo gem install rugged'
|
|
8
|
-
end
|
|
2
|
+
module Output
|
|
3
|
+
class Git < Output
|
|
4
|
+
using Refinements
|
|
9
5
|
|
|
10
|
-
|
|
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
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
super
|
|
14
|
-
@cfg = Oxidized.config.output.git
|
|
15
|
-
end
|
|
13
|
+
attr_reader :commitref
|
|
16
14
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
Oxidized.
|
|
20
|
-
Oxidized.asetus.user.output.git.email = 'o@example.com'
|
|
21
|
-
Oxidized.asetus.user.output.git.repo = File.join(Config::Root, 'oxidized.git')
|
|
22
|
-
Oxidized.asetus.save :user
|
|
23
|
-
raise NoConfig, 'no output git config, edit ~/.config/oxidized/config'
|
|
15
|
+
def initialize
|
|
16
|
+
super
|
|
17
|
+
@cfg = Oxidized.config.output.git
|
|
24
18
|
end
|
|
25
19
|
|
|
26
|
-
|
|
27
|
-
@cfg.
|
|
28
|
-
|
|
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}"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
if @cfg.repo.respond_to?(:each)
|
|
30
|
+
@cfg.repo.each do |group, repo|
|
|
31
|
+
@cfg.repo["#{group}="] = ::File.expand_path repo
|
|
32
|
+
end
|
|
33
|
+
else
|
|
34
|
+
@cfg.repo = ::File.expand_path @cfg.repo
|
|
29
35
|
end
|
|
30
|
-
else
|
|
31
|
-
@cfg.repo = File.expand_path @cfg.repo
|
|
32
36
|
end
|
|
33
|
-
end
|
|
34
37
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
38
|
+
# file: node name (String)
|
|
39
|
+
# outputs: Oxidized::Models::Outputs
|
|
40
|
+
# opts: dict of optional parameters:
|
|
41
|
+
# - msg: commit message
|
|
42
|
+
# - email: committer email
|
|
43
|
+
# - user: committer name
|
|
44
|
+
# - group: node group
|
|
45
|
+
# - significant_changes:
|
|
46
|
+
# nil / not set / true: store as usual
|
|
47
|
+
# false: skip general config, only store configs where type != nil
|
|
48
|
+
def store(file, outputs, opt = {})
|
|
49
|
+
@msg = opt[:msg]
|
|
50
|
+
@user = opt[:user] || @cfg.user
|
|
51
|
+
@email = opt[:email] || @cfg.email
|
|
52
|
+
@opt = opt
|
|
53
|
+
@commitref = nil
|
|
54
|
+
repo = @cfg.repo
|
|
55
|
+
|
|
56
|
+
outputs.types.each do |type|
|
|
57
|
+
type_cfg = ''
|
|
58
|
+
type_repo = ::File.join(::File.dirname(repo), type + '.git')
|
|
59
|
+
outputs.type(type).each do |output|
|
|
60
|
+
(type_cfg << output; next) unless output.name # rubocop:disable Style/Semicolon
|
|
61
|
+
type_file = file + '--' + output.name
|
|
62
|
+
if @cfg.type_as_directory?
|
|
63
|
+
type_file = type + '/' + type_file
|
|
64
|
+
type_repo = repo
|
|
65
|
+
end
|
|
66
|
+
update type_repo, type_file, output
|
|
52
67
|
end
|
|
53
|
-
update type_repo,
|
|
68
|
+
update type_repo, file, type_cfg
|
|
54
69
|
end
|
|
55
|
-
|
|
70
|
+
|
|
71
|
+
update repo, file, outputs.to_cfg unless opt[:significant_changes] == false
|
|
56
72
|
end
|
|
57
73
|
|
|
58
|
-
|
|
59
|
-
|
|
74
|
+
# Returns the configuration of group/node_name
|
|
75
|
+
#
|
|
76
|
+
# #fetch is called by Nodes#fetch
|
|
77
|
+
# Nodes#fetch creates a new Output object each time, so it not easy
|
|
78
|
+
# to cache the repo index in memory. But as we keep the repo index up
|
|
79
|
+
# to date on disk in #update_repo, we can read it from disk instead of
|
|
80
|
+
# rebuilding it each time.
|
|
81
|
+
def fetch(node, group)
|
|
82
|
+
repo, path = yield_repo_and_path(node, group)
|
|
83
|
+
repo = Rugged::Repository.new repo
|
|
84
|
+
# Read the index from disk
|
|
85
|
+
index = repo.index
|
|
60
86
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
index.read_tree repo.head.target.tree unless repo.empty?
|
|
66
|
-
repo.read(index.get(path)[:oid]).data
|
|
67
|
-
rescue StandardError
|
|
68
|
-
'node not found'
|
|
69
|
-
end
|
|
87
|
+
repo.read(index.get(path)[:oid]).data
|
|
88
|
+
rescue StandardError
|
|
89
|
+
'node not found'
|
|
90
|
+
end
|
|
70
91
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
tab = []
|
|
81
|
-
walker.each do |commit|
|
|
82
|
-
# Diabled rubocop because the suggested .empty? does not work here.
|
|
83
|
-
next if commit.diff(paths: [path]).size.zero? # rubocop:disable Style/ZeroLengthPredicate
|
|
84
|
-
|
|
85
|
-
hash = {}
|
|
86
|
-
hash[:date] = commit.time.to_s
|
|
87
|
-
hash[:oid] = commit.oid
|
|
88
|
-
hash[:author] = commit.author
|
|
89
|
-
hash[:message] = commit.message
|
|
90
|
-
tab[i += 1] = hash
|
|
92
|
+
# give a hash of all oid revisions for the given node, and the date of
|
|
93
|
+
# the commit.
|
|
94
|
+
#
|
|
95
|
+
# Called by Nodes#version
|
|
96
|
+
def version(node, group)
|
|
97
|
+
repo_path, node_path = yield_repo_and_path(node, group)
|
|
98
|
+
self.class.hash_list(node_path, repo_path)
|
|
99
|
+
rescue StandardError
|
|
100
|
+
'node not found'
|
|
91
101
|
end
|
|
92
|
-
walker.reset
|
|
93
|
-
tab
|
|
94
|
-
rescue StandardError
|
|
95
|
-
'node not found'
|
|
96
|
-
end
|
|
97
102
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
103
|
+
# give the blob of a specific revision
|
|
104
|
+
def get_version(node, group, oid)
|
|
105
|
+
repo, path = yield_repo_and_path(node, group)
|
|
106
|
+
repo = Rugged::Repository.new repo
|
|
107
|
+
repo.blob_at(oid, path).content
|
|
108
|
+
rescue StandardError
|
|
109
|
+
'version not found'
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# give a hash with the patch of a diff between 2 revision and the stats (added and deleted lines)
|
|
113
|
+
def get_diff(node, group, oid1, oid2)
|
|
114
|
+
diff_commits = nil
|
|
115
|
+
repo, = yield_repo_and_path(node, group)
|
|
116
|
+
repo = Rugged::Repository.new repo
|
|
117
|
+
commit = repo.lookup(oid1)
|
|
118
|
+
|
|
119
|
+
if oid2
|
|
120
|
+
commit_old = repo.lookup(oid2)
|
|
121
|
+
diff = repo.diff(commit_old, commit)
|
|
122
|
+
diff.each do |patch|
|
|
123
|
+
if /#{node.name}\s+/ =~ patch.to_s.lines.first
|
|
124
|
+
diff_commits = { patch: patch.to_s, stat: patch.stat }
|
|
125
|
+
break
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
else
|
|
129
|
+
stat = commit.parents[0].diff(commit).stat
|
|
130
|
+
stat = [stat[1], stat[2]]
|
|
131
|
+
patch = commit.parents[0].diff(commit).patch
|
|
132
|
+
diff_commits = { patch: patch, stat: stat }
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
diff_commits
|
|
136
|
+
rescue StandardError
|
|
137
|
+
'no diffs'
|
|
138
|
+
end
|
|
106
139
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
140
|
+
# Return the list of oids for node_path in the repository repo_path
|
|
141
|
+
def self.hash_list(node_path, repo_path)
|
|
142
|
+
update_cache(repo_path)
|
|
143
|
+
@gitcache[repo_path][:nodes][node_path] || []
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Update @gitcache, a class instance variable, ensuring persistence
|
|
147
|
+
# by saving the cache independently of object instances
|
|
148
|
+
def self.update_cache(repo_path)
|
|
149
|
+
# initialize our cache as a class instance variable
|
|
150
|
+
@gitcache ||= {}
|
|
151
|
+
# When single_repo == false, we have multiple repositories
|
|
152
|
+
unless @gitcache[repo_path]
|
|
153
|
+
@gitcache[repo_path] = {}
|
|
154
|
+
@gitcache[repo_path][:nodes] = {}
|
|
155
|
+
@gitcache[repo_path][:last_commit] = nil
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
repo = Rugged::Repository.new repo_path
|
|
159
|
+
|
|
160
|
+
walker = Rugged::Walker.new(repo)
|
|
161
|
+
walker.sorting(Rugged::SORT_DATE)
|
|
162
|
+
walker.push(repo.head.target.oid)
|
|
163
|
+
|
|
164
|
+
# We store the commits into a temporary cache. It will be prepended
|
|
165
|
+
# to @gitcache to preserve the order of the commits.
|
|
166
|
+
cache = {}
|
|
167
|
+
walker.each do |commit|
|
|
168
|
+
if commit.oid == @gitcache[repo_path][:last_commit]
|
|
169
|
+
# we have reached the last cached commit, so we're done
|
|
120
170
|
break
|
|
121
171
|
end
|
|
172
|
+
|
|
173
|
+
commit.diff.each_delta do |delta|
|
|
174
|
+
next unless delta.added? || delta.modified?
|
|
175
|
+
|
|
176
|
+
hash = {}
|
|
177
|
+
# We keep :date for reverse compatibility on oxidized-web <= 0.15.1
|
|
178
|
+
hash[:date] = commit.time.to_s
|
|
179
|
+
# date as a Time instance for more flexibility in oxidized-web
|
|
180
|
+
hash[:time] = commit.time
|
|
181
|
+
hash[:oid] = commit.oid
|
|
182
|
+
hash[:author] = commit.author
|
|
183
|
+
hash[:message] = commit.message
|
|
184
|
+
|
|
185
|
+
filename = delta.new_file[:path]
|
|
186
|
+
if cache[filename]
|
|
187
|
+
cache[filename].append hash
|
|
188
|
+
else
|
|
189
|
+
cache[filename] = [hash]
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
cache.each_pair do |filename, hashlist|
|
|
195
|
+
if @gitcache[repo_path][:nodes][filename]
|
|
196
|
+
# using the splat operator (*) should be OK as hashlist should
|
|
197
|
+
# not be very big when working on deltas
|
|
198
|
+
@gitcache[repo_path][:nodes][filename].prepend(*hashlist)
|
|
199
|
+
else
|
|
200
|
+
@gitcache[repo_path][:nodes][filename] = hashlist
|
|
201
|
+
end
|
|
122
202
|
end
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
patch = commit.parents[0].diff(commit).patch
|
|
127
|
-
diff_commits = { patch: patch, stat: stat }
|
|
203
|
+
|
|
204
|
+
# Store the most recent commit
|
|
205
|
+
@gitcache[repo_path][:last_commit] = repo.head.target.oid
|
|
128
206
|
end
|
|
129
207
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
208
|
+
# Currently only used in unit tests
|
|
209
|
+
def self.clear_cache
|
|
210
|
+
@gitcache = nil
|
|
211
|
+
end
|
|
134
212
|
|
|
135
|
-
|
|
213
|
+
def self.clean_obsolete_nodes(active_nodes)
|
|
214
|
+
git_config = Oxidized.config.output.git
|
|
215
|
+
repo_path = git_config.repo
|
|
136
216
|
|
|
137
|
-
|
|
138
|
-
|
|
217
|
+
unless git_config.single_repo?
|
|
218
|
+
logger.warn "clean_obsolete_nodes is not implemented for " \
|
|
219
|
+
"multiple git repositories"
|
|
220
|
+
return
|
|
221
|
+
end
|
|
139
222
|
|
|
140
|
-
|
|
223
|
+
if git_config.type_as_directory?
|
|
224
|
+
logger.warn "clean_obsolete_nodes is not implemented for output " \
|
|
225
|
+
"types as a directory within the git repository"
|
|
226
|
+
return
|
|
227
|
+
end
|
|
141
228
|
|
|
142
|
-
|
|
143
|
-
|
|
229
|
+
# The repo might not exist on the first run
|
|
230
|
+
return unless ::File.directory?(repo_path)
|
|
144
231
|
|
|
145
|
-
|
|
146
|
-
|
|
232
|
+
repo = Rugged::Repository.new repo_path
|
|
233
|
+
return if repo.empty?
|
|
147
234
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
235
|
+
keep_files = active_nodes.map do |n|
|
|
236
|
+
n.group ? ::File.join(n.group, n.name) : n.name
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
tree = repo.last_commit.tree
|
|
240
|
+
files_to_delete = []
|
|
241
|
+
|
|
242
|
+
tree.walk_blobs do |root, entry|
|
|
243
|
+
file_path = root.empty? ? entry[:name] : ::File.join(root, entry[:name])
|
|
244
|
+
files_to_delete << file_path unless keep_files.include?(file_path)
|
|
157
245
|
end
|
|
246
|
+
|
|
247
|
+
return if files_to_delete.empty?
|
|
248
|
+
|
|
249
|
+
logger.info "clean_obsolete_nodes: removing " \
|
|
250
|
+
"#{files_to_delete.size} obsolete configs"
|
|
251
|
+
index = repo.index
|
|
252
|
+
|
|
253
|
+
files_to_delete.each { |file_path| index.remove(file_path) }
|
|
254
|
+
|
|
255
|
+
repo.config['user.name'] = git_config.user
|
|
256
|
+
repo.config['user.email'] = git_config.email
|
|
257
|
+
Rugged::Commit.create(
|
|
258
|
+
repo,
|
|
259
|
+
tree: index.write_tree(repo),
|
|
260
|
+
message: "Removing #{files_to_delete.size} obsolete configs",
|
|
261
|
+
parents: [repo.head.target].compact,
|
|
262
|
+
update_ref: 'HEAD'
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
index.write
|
|
158
266
|
end
|
|
159
267
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
268
|
+
private
|
|
269
|
+
|
|
270
|
+
def yield_repo_and_path(node, group)
|
|
271
|
+
repo = node.repo
|
|
272
|
+
path = node.name
|
|
273
|
+
|
|
274
|
+
path = "#{group}/#{node.name}" if group && !group.empty? && @cfg.single_repo?
|
|
275
|
+
|
|
276
|
+
[repo, path]
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
def update(repo, file, data)
|
|
280
|
+
return if data.empty?
|
|
281
|
+
|
|
282
|
+
if @opt[:group]
|
|
283
|
+
if @cfg.single_repo?
|
|
284
|
+
file = ::File.join @opt[:group], file
|
|
285
|
+
else
|
|
286
|
+
repo = if repo.is_a?(::String)
|
|
287
|
+
::File.join ::File.dirname(repo), @opt[:group] + '.git'
|
|
288
|
+
else
|
|
289
|
+
repo[@opt[:group]]
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
|
|
164
294
|
begin
|
|
165
|
-
Rugged::Repository.
|
|
166
|
-
|
|
167
|
-
|
|
295
|
+
repo = Rugged::Repository.new repo
|
|
296
|
+
update_repo repo, file, data
|
|
297
|
+
rescue Rugged::OSError, Rugged::RepositoryError => e
|
|
298
|
+
begin
|
|
299
|
+
Rugged::Repository.init_at repo, :bare
|
|
300
|
+
rescue StandardError => create_error
|
|
301
|
+
raise GitError, "first '#{e.message}' was raised while opening git repo, then '#{create_error.message}' " \
|
|
302
|
+
"was while trying to create git repo"
|
|
303
|
+
end
|
|
304
|
+
retry
|
|
168
305
|
end
|
|
169
|
-
retry
|
|
170
306
|
end
|
|
171
|
-
end
|
|
172
307
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
308
|
+
# Uploads data into file in the repository repo
|
|
309
|
+
#
|
|
310
|
+
# update_repo caches the index on disk. An index is usually used in a
|
|
311
|
+
# working directory and not in a bare repository, which confuses users.
|
|
312
|
+
# The alternative would be to rebuild the index each time, which a little
|
|
313
|
+
# time consuming. Caching the index in memory is difficult because a new
|
|
314
|
+
# Output object is created each time #store is called.
|
|
315
|
+
def update_repo(repo, file, data) # rubocop:disable Naming/PredicateMethod
|
|
316
|
+
oid_old = repo.blob_at(repo.head.target_id, file) rescue nil
|
|
317
|
+
return false if oid_old && (oid_old.content.b == data.b)
|
|
176
318
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
319
|
+
oid = repo.write data, :blob
|
|
320
|
+
# Read the index from disk
|
|
321
|
+
index = repo.index
|
|
322
|
+
index.add path: file, oid: oid, mode: 0o100644
|
|
180
323
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
324
|
+
repo.config['user.name'] = @user
|
|
325
|
+
repo.config['user.email'] = @email
|
|
326
|
+
@commitref = Rugged::Commit.create(repo,
|
|
327
|
+
tree: index.write_tree(repo),
|
|
328
|
+
message: @msg,
|
|
329
|
+
parents: repo.empty? ? [] : [repo.head.target].compact,
|
|
330
|
+
update_ref: 'HEAD')
|
|
188
331
|
|
|
189
|
-
|
|
190
|
-
|
|
332
|
+
index.write
|
|
333
|
+
true
|
|
334
|
+
end
|
|
191
335
|
end
|
|
192
336
|
end
|
|
193
337
|
end
|