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/extra/syslog.rb
CHANGED
|
@@ -8,8 +8,10 @@
|
|
|
8
8
|
# set system syslog host SERVER interactive-commands notice
|
|
9
9
|
# set system syslog host SERVER match "^mgd\[[0-9]+\]: UI_COMMIT: .*"
|
|
10
10
|
|
|
11
|
-
# Ports < 1024 need extra privileges, use a port higher than this by setting the
|
|
12
|
-
#
|
|
11
|
+
# Ports < 1024 need extra privileges, use a port higher than this by setting the
|
|
12
|
+
# port option in your oxidized config file.
|
|
13
|
+
# To use the default port for syslog (514) you shouldn't pass an argument, but
|
|
14
|
+
# you will need to allow this with:
|
|
13
15
|
# sudo setcap 'cap_net_bind_service=+ep' /usr/bin/ruby
|
|
14
16
|
|
|
15
17
|
# Config options are:
|
|
@@ -43,8 +45,8 @@ module Oxidized
|
|
|
43
45
|
|
|
44
46
|
begin
|
|
45
47
|
CFGS.load
|
|
46
|
-
rescue StandardError =>
|
|
47
|
-
raise InvalidConfig, "Error loading config: #{
|
|
48
|
+
rescue StandardError => e
|
|
49
|
+
raise InvalidConfig, "Error loading config: #{e.message}"
|
|
48
50
|
ensure
|
|
49
51
|
CFG = CFGS.cfg # convenienence, instead of Config.cfg.password, CFG.password
|
|
50
52
|
end
|
|
@@ -52,6 +54,7 @@ module Oxidized
|
|
|
52
54
|
class SyslogMonitor
|
|
53
55
|
MSG = {
|
|
54
56
|
ios: /%SYS-(SW[0-9]+-)?5-CONFIG_I:/,
|
|
57
|
+
iosxr: /%MGBL-SYS-5-CONFIG_I/,
|
|
55
58
|
junos: 'UI_COMMIT:',
|
|
56
59
|
eos: /%SYS-5-CONFIG_I:/,
|
|
57
60
|
nxos: /%VSHD-5-VSHD_SYSLOG_CONFIG_I:/,
|
|
@@ -89,6 +92,7 @@ module Oxidized
|
|
|
89
92
|
opts[:from] = log[-1][1..-2]
|
|
90
93
|
opts
|
|
91
94
|
end
|
|
95
|
+
alias iosxr ios
|
|
92
96
|
alias nxos ios
|
|
93
97
|
alias eos ios
|
|
94
98
|
|
|
@@ -116,7 +120,8 @@ module Oxidized
|
|
|
116
120
|
def run(io)
|
|
117
121
|
loop do
|
|
118
122
|
log = select [io]
|
|
119
|
-
log
|
|
123
|
+
log = log.first.first
|
|
124
|
+
ip = nil
|
|
120
125
|
if @mode == :udp
|
|
121
126
|
log, ip = log.recvfrom_nonblock 2000
|
|
122
127
|
ip = ip.last
|
|
@@ -136,7 +141,7 @@ module Oxidized
|
|
|
136
141
|
if Oxidized::CFG.syslogd.resolve == false
|
|
137
142
|
ipaddr
|
|
138
143
|
else
|
|
139
|
-
name = (Resolv.getname ipaddr.to_s rescue ipaddr)
|
|
144
|
+
name = (Resolv.getname ipaddr.to_s rescue ipaddr) # rubocop:disable Style/RedundantParentheses
|
|
140
145
|
Oxidized::CFG.syslogd.dns_map.each { |re, sub| name.sub! Regexp.new(re.to_s), sub }
|
|
141
146
|
name
|
|
142
147
|
end
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
require 'time'
|
|
2
|
+
|
|
3
|
+
module Oxidized
|
|
4
|
+
class CLI
|
|
5
|
+
module Support
|
|
6
|
+
SENSITIVE_NAME_RE = /(password|passphrase|secret|token|enable|
|
|
7
|
+
(private|api|access)_?key|
|
|
8
|
+
community|credential|auth
|
|
9
|
+
)/ix
|
|
10
|
+
ROOT_GEMS = %w[oxidized oxidized-web].freeze
|
|
11
|
+
EXPLICIT_ENV_KEYS = %w[
|
|
12
|
+
OXIDIZED_HOME
|
|
13
|
+
OXIDIZED_LOGS
|
|
14
|
+
CONFIG_RELOAD_INTERVAL
|
|
15
|
+
UPDATE_CA_CERTIFICATES
|
|
16
|
+
].freeze
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def show_support_details
|
|
21
|
+
print_intro
|
|
22
|
+
print_environment
|
|
23
|
+
print_config_files
|
|
24
|
+
print_rugged_support
|
|
25
|
+
print_installed_gems
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def print_intro
|
|
29
|
+
os_release = read_os_release
|
|
30
|
+
runit_path = '/etc/service/oxidized/run'
|
|
31
|
+
|
|
32
|
+
puts '> :warning:'
|
|
33
|
+
puts '> The --support option is intended for diagnostic purposes and may include sensitive information.'
|
|
34
|
+
puts '> Remove any sensitive data before sharing this output.'
|
|
35
|
+
puts
|
|
36
|
+
puts '## Oxidized Support Data'
|
|
37
|
+
puts "- Timestamp: #{Time.now.utc.iso8601}"
|
|
38
|
+
puts "- Oxidized version: #{Oxidized::VERSION_FULL}"
|
|
39
|
+
puts "- OS release: #{os_release}" if os_release
|
|
40
|
+
puts "- Container hint (#{runit_path} exists): #{File.exist?(runit_path)}"
|
|
41
|
+
puts "- Ruby engine: #{defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby'}"
|
|
42
|
+
puts "- Ruby version: #{RUBY_VERSION}p#{RUBY_PATCHLEVEL} (#{RUBY_PLATFORM})"
|
|
43
|
+
puts "- Working directory: #{Dir.pwd}"
|
|
44
|
+
puts "- Gem paths: #{Gem.path.join(', ')}"
|
|
45
|
+
puts
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def print_environment
|
|
49
|
+
puts '### Environment Variables'
|
|
50
|
+
keys = (ENV.keys.grep(/^OXIDIZED_/) + EXPLICIT_ENV_KEYS).uniq.sort
|
|
51
|
+
|
|
52
|
+
keys.each do |key|
|
|
53
|
+
next unless ENV.has_key?(key)
|
|
54
|
+
|
|
55
|
+
value = ENV.fetch(key)
|
|
56
|
+
puts key.match?(SENSITIVE_NAME_RE) ? "#{key}=[REDACTED]" : "#{key}=#{value}"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
puts
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def print_config_files
|
|
63
|
+
puts '### Configuration Files'
|
|
64
|
+
config_paths.each do |path|
|
|
65
|
+
config_path = File.expand_path(path)
|
|
66
|
+
exists = File.exist?(config_path)
|
|
67
|
+
puts "- #{config_path} exists: #{exists ? 'yes' : 'no'}"
|
|
68
|
+
next unless exists
|
|
69
|
+
|
|
70
|
+
print_sanitized_config(config_path)
|
|
71
|
+
end
|
|
72
|
+
puts
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def print_rugged_support
|
|
76
|
+
puts '### Rugged'
|
|
77
|
+
begin
|
|
78
|
+
require 'rugged'
|
|
79
|
+
puts "- Rugged version: #{Rugged::VERSION}"
|
|
80
|
+
|
|
81
|
+
ssh_supported = Rugged.respond_to?(:features) && Rugged.features.include?(:ssh)
|
|
82
|
+
puts "- Rugged SSH support: #{ssh_supported}"
|
|
83
|
+
rescue LoadError
|
|
84
|
+
puts '- Rugged: not available'
|
|
85
|
+
puts '- Rugged SSH support: false'
|
|
86
|
+
end
|
|
87
|
+
puts
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def print_installed_gems
|
|
91
|
+
puts '### Relevant Installed Gems'
|
|
92
|
+
relevant_gem_names.each do |name|
|
|
93
|
+
versions = Gem::Specification.find_all_by_name(name).sort_by(&:version).map { |s| s.version.to_s }
|
|
94
|
+
puts "- #{name} (#{versions.join(', ')})" unless versions.empty?
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def relevant_gem_names
|
|
99
|
+
names = ROOT_GEMS.select { |name| Gem::Specification.any? { |s| s.name == name } }
|
|
100
|
+
|
|
101
|
+
root_specs = names.flat_map { |name| Gem::Specification.find_all_by_name(name) }
|
|
102
|
+
runtime_deps = root_specs.flat_map { |spec| spec.dependencies }
|
|
103
|
+
names.concat(runtime_deps.map(&:name))
|
|
104
|
+
names.sort.uniq
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def config_paths
|
|
108
|
+
user_default = File.join(Dir.home, '.config', 'oxidized', 'config')
|
|
109
|
+
home_from_env = File.join(File.expand_path(Oxidized::Config::ROOT), 'config')
|
|
110
|
+
|
|
111
|
+
[
|
|
112
|
+
'/etc/oxidized/config',
|
|
113
|
+
user_default,
|
|
114
|
+
home_from_env
|
|
115
|
+
].uniq
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def print_sanitized_config(path)
|
|
119
|
+
content = File.read(path)
|
|
120
|
+
puts '```yaml'
|
|
121
|
+
content.each_line(chomp: true) do |line|
|
|
122
|
+
key, separator, = line.partition(':')
|
|
123
|
+
|
|
124
|
+
if separator.empty? || key.empty?
|
|
125
|
+
puts line
|
|
126
|
+
next
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
if key.match?(SENSITIVE_NAME_RE)
|
|
130
|
+
puts "#{key}: [REDACTED]"
|
|
131
|
+
else
|
|
132
|
+
puts line
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
puts '```'
|
|
136
|
+
rescue StandardError => e
|
|
137
|
+
puts " <failed to read: #{e.class}: #{e.message}>"
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def read_os_release
|
|
141
|
+
return nil unless File.exist?('/etc/os-release')
|
|
142
|
+
|
|
143
|
+
line = File.foreach('/etc/os-release').find { |entry| entry.start_with?('PRETTY_NAME=') }
|
|
144
|
+
return nil unless line
|
|
145
|
+
|
|
146
|
+
line.split('=', 2).last.to_s.strip.gsub(/^"|"$/, '')
|
|
147
|
+
rescue StandardError
|
|
148
|
+
nil
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
data/lib/oxidized/cli.rb
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
|
+
require 'semantic_logger'
|
|
2
|
+
require_relative 'cli/support'
|
|
3
|
+
|
|
1
4
|
module Oxidized
|
|
2
5
|
class CLI
|
|
6
|
+
include SemanticLogger::Loggable
|
|
7
|
+
include Support
|
|
8
|
+
|
|
3
9
|
require 'slop'
|
|
4
10
|
require 'oxidized'
|
|
5
11
|
require 'English'
|
|
@@ -9,10 +15,10 @@ module Oxidized
|
|
|
9
15
|
Process.daemon if @opts[:daemonize]
|
|
10
16
|
write_pid
|
|
11
17
|
begin
|
|
12
|
-
|
|
18
|
+
logger.info "Oxidized starting, running as pid #{$PROCESS_ID}"
|
|
13
19
|
Oxidized.new
|
|
14
|
-
rescue StandardError =>
|
|
15
|
-
crash
|
|
20
|
+
rescue StandardError => e
|
|
21
|
+
crash e
|
|
16
22
|
raise
|
|
17
23
|
end
|
|
18
24
|
end
|
|
@@ -23,14 +29,14 @@ module Oxidized
|
|
|
23
29
|
_args, @opts = parse_opts
|
|
24
30
|
|
|
25
31
|
Config.load(@opts)
|
|
26
|
-
Oxidized.
|
|
32
|
+
Oxidized::Logger.setup
|
|
27
33
|
|
|
28
34
|
@pidfile = File.expand_path(Oxidized.config.pid)
|
|
29
35
|
end
|
|
30
36
|
|
|
31
37
|
def crash(error)
|
|
32
|
-
|
|
33
|
-
File.open Config::
|
|
38
|
+
logger.fatal "Oxidized crashed, crashfile written in #{Config::CRASH}"
|
|
39
|
+
File.open Config::CRASH, 'w' do |file|
|
|
34
40
|
file.puts '-' * 50
|
|
35
41
|
file.puts Time.now.utc
|
|
36
42
|
file.puts error.message + ' [' + error.class.to_s + ']'
|
|
@@ -44,6 +50,9 @@ module Oxidized
|
|
|
44
50
|
opts = Slop.parse do |opt|
|
|
45
51
|
opt.on '-d', '--debug', 'turn on debugging'
|
|
46
52
|
opt.on '--daemonize', 'Daemonize/fork the process'
|
|
53
|
+
opt.on '--support', 'show support diagnostics and exit'
|
|
54
|
+
opt.string '--home-dir', 'Oxidized home dir', default: nil
|
|
55
|
+
opt.string '--config-file', 'Oxidized config file', default: nil
|
|
47
56
|
opt.on '-h', '--help', 'show usage' do
|
|
48
57
|
puts opt
|
|
49
58
|
exit
|
|
@@ -58,6 +67,12 @@ module Oxidized
|
|
|
58
67
|
Kernel.exit
|
|
59
68
|
end
|
|
60
69
|
end
|
|
70
|
+
|
|
71
|
+
if opts[:support]
|
|
72
|
+
show_support_details
|
|
73
|
+
Kernel.exit
|
|
74
|
+
end
|
|
75
|
+
|
|
61
76
|
[opts.arguments, opts]
|
|
62
77
|
end
|
|
63
78
|
|
data/lib/oxidized/config/vars.rb
CHANGED
|
@@ -1,17 +1,36 @@
|
|
|
1
|
-
module Oxidized
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
module Oxidized
|
|
2
|
+
class Config
|
|
3
|
+
module Vars
|
|
4
|
+
# convenience method for accessing node, group or global level user variables
|
|
5
|
+
def vars(name)
|
|
6
|
+
model_name = @node.model.class.name.to_s.downcase
|
|
7
|
+
groups = Oxidized.config.groups
|
|
8
|
+
models = Oxidized.config.models
|
|
9
|
+
group = groups[@node.group] if groups.has_key?(@node.group)
|
|
10
|
+
model = models[model_name] if models.has_key?(model_name)
|
|
11
|
+
group_model = group.models[model_name] if group&.models&.has_key?(model_name)
|
|
12
|
+
|
|
13
|
+
scopes = {
|
|
14
|
+
node: @node.vars,
|
|
15
|
+
group_model: group_model&.vars,
|
|
16
|
+
group: group&.vars,
|
|
17
|
+
model: model&.vars,
|
|
18
|
+
vars: Oxidized.config.vars
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
scopes.each do |scope_name, scope|
|
|
22
|
+
next unless scope&.has_key?(name.to_s)
|
|
23
|
+
|
|
24
|
+
val = scope[name.to_s]
|
|
25
|
+
if val.nil?
|
|
26
|
+
Oxidized.logger.debug "vars.rb: scope #{scope_name} has key #{name} with value nil, ignoring scope"
|
|
27
|
+
else
|
|
28
|
+
Oxidized.logger.debug "vars.rb: scope #{scope_name} has key #{name} with value #{val}, using scope"
|
|
29
|
+
return val
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
nil
|
|
33
|
+
end
|
|
15
34
|
end
|
|
16
35
|
end
|
|
17
36
|
end
|
data/lib/oxidized/config.rb
CHANGED
|
@@ -4,18 +4,22 @@ module Oxidized
|
|
|
4
4
|
class InvalidConfig < OxidizedError; end
|
|
5
5
|
|
|
6
6
|
class Config
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
7
|
+
ROOT = ENV['OXIDIZED_HOME'] || File.join(Dir.home, '.config', 'oxidized')
|
|
8
|
+
CRASH = File.join(ENV['OXIDIZED_LOGS'] || ROOT, 'crash')
|
|
9
|
+
LOG = File.join(ENV['OXIDIZED_LOGS'] || ROOT, 'logs')
|
|
10
|
+
INPUT_DIR = File.join Directory, %w[lib oxidized input]
|
|
11
|
+
OUTPUT_DIR = File.join Directory, %w[lib oxidized output]
|
|
12
|
+
MODEL_DIR = File.join Directory, %w[lib oxidized model]
|
|
13
|
+
SOURCE_DIR = File.join Directory, %w[lib oxidized source]
|
|
14
|
+
HOOK_DIR = File.join Directory, %w[lib oxidized hook]
|
|
15
|
+
SLEEP = 1
|
|
16
16
|
|
|
17
17
|
def self.load(cmd_opts = {})
|
|
18
|
-
|
|
18
|
+
usrdir = File.expand_path(cmd_opts[:home_dir] || Oxidized::Config::ROOT)
|
|
19
|
+
cfgfile = cmd_opts[:config_file] || 'config'
|
|
20
|
+
# configuration file with full path as a class instance variable
|
|
21
|
+
@configfile = File.join(usrdir, cfgfile)
|
|
22
|
+
asetus = Asetus.new(name: 'oxidized', load: false, usrdir: usrdir, cfgfile: cfgfile)
|
|
19
23
|
Oxidized.asetus = asetus
|
|
20
24
|
|
|
21
25
|
asetus.default.username = 'username'
|
|
@@ -23,22 +27,25 @@ module Oxidized
|
|
|
23
27
|
asetus.default.model = 'junos'
|
|
24
28
|
asetus.default.resolve_dns = true # if false, don't resolve DNS to IP
|
|
25
29
|
asetus.default.interval = 3600
|
|
26
|
-
asetus.default.use_syslog = false
|
|
27
30
|
asetus.default.debug = false
|
|
31
|
+
asetus.default.run_once = false
|
|
28
32
|
asetus.default.threads = 30
|
|
29
33
|
asetus.default.use_max_threads = false
|
|
30
34
|
asetus.default.timeout = 20
|
|
35
|
+
asetus.default.timelimit = 300
|
|
31
36
|
asetus.default.retries = 3
|
|
32
37
|
asetus.default.prompt = /^([\w.@-]+[#>]\s?)$/
|
|
33
|
-
asetus.default.rest = '127.0.0.1:8888' # or false to disable
|
|
34
38
|
asetus.default.next_adds_job = false # if true, /next adds job, so device is fetched immmeiately
|
|
35
39
|
asetus.default.vars = {} # could be 'enable'=>'enablePW'
|
|
36
40
|
asetus.default.groups = {} # group level configuration
|
|
37
41
|
asetus.default.group_map = {} # map aliases of groups to names
|
|
38
42
|
asetus.default.models = {} # model level configuration
|
|
39
|
-
asetus.default.pid = File.join(Oxidized::Config::
|
|
43
|
+
asetus.default.pid = File.join(Oxidized::Config::ROOT, 'pid')
|
|
40
44
|
|
|
41
|
-
|
|
45
|
+
# Extentions
|
|
46
|
+
asetus.default.extensions['oxidized-web'].load = false
|
|
47
|
+
|
|
48
|
+
asetus.default.crash.directory = File.join(Oxidized::Config::ROOT, 'crashes')
|
|
42
49
|
asetus.default.crash.hostnames = false
|
|
43
50
|
|
|
44
51
|
asetus.default.stats.history_size = 10
|
|
@@ -58,17 +65,21 @@ module Oxidized
|
|
|
58
65
|
|
|
59
66
|
begin
|
|
60
67
|
asetus.load # load system+user configs, merge to Config.cfg
|
|
61
|
-
rescue StandardError =>
|
|
62
|
-
raise InvalidConfig, "Error loading config: #{
|
|
68
|
+
rescue StandardError => e
|
|
69
|
+
raise InvalidConfig, "Error loading config: #{e.message}"
|
|
63
70
|
end
|
|
64
71
|
|
|
65
|
-
raise NoConfig,
|
|
72
|
+
raise NoConfig, "edit #{@configfile}" if asetus.create
|
|
66
73
|
|
|
67
74
|
# override if comand line flag given
|
|
68
75
|
asetus.cfg.debug = cmd_opts[:debug] if cmd_opts[:debug]
|
|
69
76
|
|
|
70
77
|
asetus
|
|
71
78
|
end
|
|
79
|
+
|
|
80
|
+
class << self
|
|
81
|
+
attr_reader :configfile
|
|
82
|
+
end
|
|
72
83
|
end
|
|
73
84
|
|
|
74
85
|
class << self
|
data/lib/oxidized/core.rb
CHANGED
|
@@ -6,24 +6,56 @@ module Oxidized
|
|
|
6
6
|
end
|
|
7
7
|
|
|
8
8
|
class Core
|
|
9
|
+
include SemanticLogger::Loggable
|
|
10
|
+
|
|
9
11
|
class NoNodesFound < OxidizedError; end
|
|
10
12
|
|
|
11
13
|
def initialize(_args)
|
|
12
14
|
Oxidized.mgr = Manager.new
|
|
13
15
|
Oxidized.hooks = HookManager.from_config(Oxidized.config)
|
|
14
16
|
nodes = Nodes.new
|
|
15
|
-
raise NoNodesFound, 'source returns no usable nodes' if nodes.
|
|
17
|
+
raise NoNodesFound, 'source returns no usable nodes' if nodes.empty?
|
|
16
18
|
|
|
17
19
|
@worker = Worker.new nodes
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
@need_reload = false
|
|
21
|
+
|
|
22
|
+
# If we receive a SIGHUP, queue a reload of the state
|
|
23
|
+
reload_proc = proc do
|
|
24
|
+
@need_reload = true
|
|
25
|
+
end
|
|
26
|
+
Signals.register_signal('HUP', reload_proc)
|
|
27
|
+
|
|
28
|
+
# Load extensions, currently only oxidized-web
|
|
29
|
+
# We have different namespaces for oxidized-web, which needs to be
|
|
30
|
+
# adressed if we need a generic way to load extensions:
|
|
31
|
+
# - gem: oxidized-web
|
|
32
|
+
# - module: Oxidized::API
|
|
33
|
+
# - path: oxidized/web
|
|
34
|
+
# - entrypoint: Oxidized::API::Web.new(nodes, configuration)
|
|
35
|
+
|
|
36
|
+
# Initialize oxidized-web if requested
|
|
37
|
+
if Oxidized.config.has_key? 'rest'
|
|
38
|
+
logger.warn(
|
|
39
|
+
'configuration: "rest" is deprecated. Migrate to ' \
|
|
40
|
+
'"extensions.oxidized-web" and remove "rest" from the configuration'
|
|
41
|
+
)
|
|
42
|
+
configuration = Oxidized.config.rest
|
|
43
|
+
elsif Oxidized.config.extensions &&
|
|
44
|
+
Oxidized.config.extensions['oxidized-web']&.load?
|
|
45
|
+
# This comment stops rubocop complaining about Style/IfUnlessModifier
|
|
46
|
+
configuration = Oxidized.config.extensions['oxidized-web']
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
if configuration
|
|
20
50
|
begin
|
|
21
51
|
require 'oxidized/web'
|
|
22
52
|
rescue LoadError
|
|
23
|
-
raise OxidizedError,
|
|
24
|
-
|
|
53
|
+
raise OxidizedError,
|
|
54
|
+
'oxidized-web not found: install it or disable it by ' \
|
|
55
|
+
'removing "rest" and "extensions.oxidized-web" from your ' \
|
|
56
|
+
'configuration'
|
|
25
57
|
end
|
|
26
|
-
@rest = API::Web.new nodes,
|
|
58
|
+
@rest = API::Web.new nodes, configuration
|
|
27
59
|
@rest.run
|
|
28
60
|
end
|
|
29
61
|
run
|
|
@@ -31,9 +63,19 @@ module Oxidized
|
|
|
31
63
|
|
|
32
64
|
private
|
|
33
65
|
|
|
66
|
+
def reload
|
|
67
|
+
logger.info("Reloading node list")
|
|
68
|
+
@worker.reload
|
|
69
|
+
@need_reload = false
|
|
70
|
+
end
|
|
71
|
+
|
|
34
72
|
def run
|
|
35
|
-
|
|
36
|
-
|
|
73
|
+
logger.debug "Starting the worker..."
|
|
74
|
+
loop do
|
|
75
|
+
reload if @need_reload
|
|
76
|
+
@worker.work
|
|
77
|
+
sleep Config::SLEEP
|
|
78
|
+
end
|
|
37
79
|
end
|
|
38
80
|
end
|
|
39
81
|
end
|
|
@@ -14,30 +14,32 @@ class CiscoSparkDiff < Oxidized::Hook
|
|
|
14
14
|
return unless ctx.node
|
|
15
15
|
return unless ctx.event.to_s == "post_store"
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
logger.info "Connecting to Cisco Spark"
|
|
18
18
|
CiscoSpark.configure do |config|
|
|
19
19
|
config.api_key = cfg.accesskey
|
|
20
20
|
config.proxy = cfg.proxy if cfg.has_key?('proxy')
|
|
21
21
|
end
|
|
22
22
|
room = CiscoSpark::Room.new(id: cfg.space)
|
|
23
|
-
|
|
23
|
+
logger.info "Connected"
|
|
24
24
|
|
|
25
25
|
if cfg.has_key?("diff") ? cfg.diff : true
|
|
26
26
|
gitoutput = ctx.node.output.new
|
|
27
27
|
diff = gitoutput.get_diff ctx.node, ctx.node.group, ctx.commitref, nil
|
|
28
28
|
title = ctx.node.name.to_s
|
|
29
|
-
|
|
30
|
-
room.send_message CiscoSpark::Message.new(text:
|
|
29
|
+
logger.info "Posting diff as snippet to #{cfg.space}"
|
|
30
|
+
room.send_message CiscoSpark::Message.new(text: "Device #{title} modified:\n" +
|
|
31
|
+
diff[:patch].lines.to_a[4..-1].join)
|
|
31
32
|
end
|
|
32
33
|
|
|
33
34
|
if cfg.message?
|
|
34
|
-
|
|
35
|
-
msg = cfg.message % { node: ctx.node.name.to_s, group: ctx.node.group.to_s, commitref: ctx.commitref,
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
logger.info cfg.message
|
|
36
|
+
msg = cfg.message % { node: ctx.node.name.to_s, group: ctx.node.group.to_s, commitref: ctx.commitref,
|
|
37
|
+
model: ctx.node.model.class.name.to_s.downcase }
|
|
38
|
+
logger.info msg
|
|
39
|
+
logger.info "Posting message to #{cfg.space}"
|
|
38
40
|
room.send_message CiscoSpark::Message.new(text: msg)
|
|
39
41
|
end
|
|
40
42
|
|
|
41
|
-
|
|
43
|
+
logger.info "Finished"
|
|
42
44
|
end
|
|
43
45
|
end
|
data/lib/oxidized/hook/exec.rb
CHANGED
|
@@ -28,7 +28,7 @@ class Exec < Oxidized::Hook
|
|
|
28
28
|
|
|
29
29
|
def run_hook(ctx)
|
|
30
30
|
env = make_env ctx
|
|
31
|
-
|
|
31
|
+
logger.debug "Execute: #{@cmd.inspect}"
|
|
32
32
|
th = Thread.new do
|
|
33
33
|
run_cmd! env
|
|
34
34
|
rescue StandardError => e
|
|
@@ -38,20 +38,21 @@ class Exec < Oxidized::Hook
|
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
def run_cmd!(env)
|
|
41
|
-
pid
|
|
41
|
+
pid = nil
|
|
42
|
+
status = nil
|
|
42
43
|
Timeout.timeout(@timeout) do
|
|
43
44
|
pid = spawn env, @cmd, unsetenv_others: true
|
|
44
45
|
pid, status = wait2 pid
|
|
45
46
|
unless status.exitstatus.zero?
|
|
46
47
|
msg = "#{@cmd.inspect} failed with exit value #{status.exitstatus}"
|
|
47
|
-
|
|
48
|
+
logger.error msg
|
|
48
49
|
raise msg
|
|
49
50
|
end
|
|
50
51
|
end
|
|
51
52
|
rescue Timeout::Error
|
|
52
53
|
kill "TERM", pid
|
|
53
54
|
msg = "#{@cmd} timed out"
|
|
54
|
-
|
|
55
|
+
logger.error msg
|
|
55
56
|
raise Timeout::Error, msg
|
|
56
57
|
end
|
|
57
58
|
|