oxidized 0.20.0 → 0.28.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.codeclimate.yml +4 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +12 -0
- data/.github/no-response.yml +13 -0
- data/.github/workflows/publishdocker.yml +13 -0
- data/.gitignore +4 -0
- data/.rubocop.yml +73 -0
- data/.rubocop_todo.yml +120 -0
- data/.travis.yml +6 -1
- data/CHANGELOG.md +693 -243
- data/Dockerfile +27 -19
- data/LICENSE +201 -0
- data/README.md +234 -913
- data/Rakefile +48 -7
- data/TODO.md +29 -23
- data/bin/console +1 -1
- data/bin/oxidized +6 -5
- data/docs/Configuration.md +313 -0
- data/docs/Creating-Models.md +140 -0
- data/docs/Hooks.md +274 -0
- data/docs/Model-Notes/AireOS.md +11 -0
- data/docs/Model-Notes/ArbOS.md +11 -0
- data/docs/Model-Notes/Comware.md +13 -0
- data/docs/Model-Notes/Cumulus.md +40 -0
- data/docs/Model-Notes/EOS.md +12 -0
- data/docs/Model-Notes/IOS.md +29 -0
- data/docs/Model-Notes/JunOS.md +33 -0
- data/docs/Model-Notes/LinuxGeneric.md +24 -0
- data/docs/Model-Notes/Netgear.md +87 -0
- data/docs/Model-Notes/Nokia.md +9 -0
- data/docs/Model-Notes/README.md +24 -0
- data/docs/Model-Notes/SmartAX-Huawei.md +35 -0
- data/docs/Model-Notes/VRP-Huawei.md +34 -0
- data/docs/Model-Notes/Viptela.md +12 -0
- data/docs/Model-Notes/XGS4600-Zyxel.md +36 -0
- data/docs/Outputs.md +190 -0
- data/docs/Ruby-API.md +199 -0
- data/docs/Sources.md +171 -0
- data/docs/Supported-OS-Types.md +227 -0
- data/docs/Troubleshooting.md +66 -0
- data/extra/nagios_check_failing_nodes.rb +9 -2
- data/extra/oxidized-report-git-commits +21 -40
- data/extra/oxidized-ubuntu.haproxy +45 -0
- data/extra/oxidized.logrotate +7 -0
- data/extra/oxidized.service +13 -0
- data/extra/rest_client.rb +7 -10
- data/extra/syslog.rb +47 -42
- data/lib/oxidized/cli.rb +41 -31
- data/lib/oxidized/config/vars.rb +9 -14
- data/lib/oxidized/config.rb +20 -13
- data/lib/oxidized/core.rb +8 -10
- data/lib/oxidized/hook/awssns.rb +6 -7
- data/lib/oxidized/hook/ciscosparkdiff.rb +43 -0
- data/lib/oxidized/hook/exec.rb +19 -24
- data/lib/oxidized/hook/githubrepo.rb +17 -17
- data/lib/oxidized/hook/noophook.rb +1 -1
- data/lib/oxidized/hook/slackdiff.rb +32 -19
- data/lib/oxidized/hook/xmppdiff.rb +59 -0
- data/lib/oxidized/hook.rb +63 -64
- data/lib/oxidized/input/cli.rb +22 -12
- data/lib/oxidized/input/exec.rb +28 -0
- data/lib/oxidized/input/ftp.rb +16 -15
- data/lib/oxidized/input/http.rb +72 -0
- data/lib/oxidized/input/input.rb +6 -6
- data/lib/oxidized/input/ssh.rb +64 -56
- data/lib/oxidized/input/telnet.rb +59 -102
- data/lib/oxidized/input/tftp.rb +9 -10
- data/lib/oxidized/jobs.rb +9 -10
- data/lib/oxidized/manager.rb +42 -44
- data/lib/oxidized/model/acos.rb +19 -20
- data/lib/oxidized/model/acsw.rb +62 -0
- data/lib/oxidized/model/adtran.rb +26 -0
- data/lib/oxidized/model/aen.rb +19 -0
- data/lib/oxidized/model/aireos.rb +9 -10
- data/lib/oxidized/model/airfiber.rb +22 -0
- data/lib/oxidized/model/alteonos.rb +58 -0
- data/lib/oxidized/model/alvarion.rb +0 -4
- data/lib/oxidized/model/aos.rb +11 -5
- data/lib/oxidized/model/aos7.rb +6 -7
- data/lib/oxidized/model/aosw.rb +30 -27
- data/lib/oxidized/model/apc_aos.rb +2 -5
- data/lib/oxidized/model/arbos.rb +26 -0
- data/lib/oxidized/model/aricentiss.rb +49 -0
- data/lib/oxidized/model/asa.rb +61 -22
- data/lib/oxidized/model/asyncos.rb +46 -0
- data/lib/oxidized/model/audiocodes.rb +28 -0
- data/lib/oxidized/model/audiocodesmp.rb +28 -0
- data/lib/oxidized/model/awplus.rb +84 -0
- data/lib/oxidized/model/axos.rb +16 -0
- data/lib/oxidized/model/boss.rb +77 -0
- data/lib/oxidized/model/br6910.rb +42 -45
- data/lib/oxidized/model/c4cmts.rb +6 -10
- data/lib/oxidized/model/cambium.rb +23 -0
- data/lib/oxidized/model/casa.rb +1 -1
- data/lib/oxidized/model/catos.rb +1 -3
- data/lib/oxidized/model/cisconga.rb +1 -3
- data/lib/oxidized/model/ciscosma.rb +42 -0
- data/lib/oxidized/model/ciscosmb.rb +30 -10
- data/lib/oxidized/model/ciscovpn3k.rb +11 -0
- data/lib/oxidized/model/cnos.rb +33 -0
- data/lib/oxidized/model/comnetms.rb +43 -0
- data/lib/oxidized/model/comtrol.rb +41 -0
- data/lib/oxidized/model/comware.rb +28 -16
- data/lib/oxidized/model/coriant8600.rb +3 -5
- data/lib/oxidized/model/coriantgroove.rb +26 -0
- data/lib/oxidized/model/corianttmos.rb +1 -3
- data/lib/oxidized/model/cumulus.rb +60 -49
- data/lib/oxidized/model/datacom.rb +1 -4
- data/lib/oxidized/model/dcnos.rb +46 -0
- data/lib/oxidized/model/dellx.rb +76 -0
- data/lib/oxidized/model/dlink.rb +5 -4
- data/lib/oxidized/model/dnos.rb +11 -5
- data/lib/oxidized/model/eciapollo.rb +34 -0
- data/lib/oxidized/model/edgecos.rb +49 -0
- data/lib/oxidized/model/edgeos.rb +12 -5
- data/lib/oxidized/model/edgeswitch.rb +2 -4
- data/lib/oxidized/model/enterasys.rb +28 -0
- data/lib/oxidized/model/eos.rb +8 -8
- data/lib/oxidized/model/fabricos.rb +4 -6
- data/lib/oxidized/model/fastiron.rb +66 -0
- data/lib/oxidized/model/fiberdriver.rb +2 -2
- data/lib/oxidized/model/firebrick.rb +31 -0
- data/lib/oxidized/model/firelinuxos.rb +41 -0
- data/lib/oxidized/model/firewareos.rb +3 -6
- data/lib/oxidized/model/fortios.rb +31 -19
- data/lib/oxidized/model/ftos.rb +8 -5
- data/lib/oxidized/model/fujitsupy.rb +5 -7
- data/lib/oxidized/model/gaiaos.rb +7 -11
- data/lib/oxidized/model/gcombnps.rb +84 -0
- data/lib/oxidized/model/grandstream.rb +9 -0
- data/lib/oxidized/model/hatteras.rb +9 -6
- data/lib/oxidized/model/hirschmann.rb +39 -0
- data/lib/oxidized/model/hpebladesystem.rb +20 -18
- data/lib/oxidized/model/hpemsa.rb +10 -0
- data/lib/oxidized/model/hpmsm.rb +84 -0
- data/lib/oxidized/model/ibos.rb +55 -0
- data/lib/oxidized/model/icotera.rb +27 -0
- data/lib/oxidized/model/ios.rb +63 -70
- data/lib/oxidized/model/iosxe.rb +5 -0
- data/lib/oxidized/model/iosxr.rb +2 -3
- data/lib/oxidized/model/ipos.rb +10 -6
- data/lib/oxidized/model/ironware.rb +20 -19
- data/lib/oxidized/model/isam.rb +5 -6
- data/lib/oxidized/model/junos.rb +9 -11
- data/lib/oxidized/model/linuxgeneric.rb +74 -0
- data/lib/oxidized/model/masteros.rb +3 -6
- data/lib/oxidized/model/mlnxos.rb +9 -10
- data/lib/oxidized/model/model.rb +72 -46
- data/lib/oxidized/model/mtrlrfs.rb +1 -4
- data/lib/oxidized/model/ndms.rb +23 -0
- data/lib/oxidized/model/netgear.rb +35 -15
- data/lib/oxidized/model/netonix.rb +2 -2
- data/lib/oxidized/model/netscaler.rb +6 -3
- data/lib/oxidized/model/nos.rb +5 -7
- data/lib/oxidized/model/nsxconfig.rb +22 -0
- data/lib/oxidized/model/nsxfirewall.rb +22 -0
- data/lib/oxidized/model/nxos.rb +13 -3
- data/lib/oxidized/model/oneos.rb +15 -9
- data/lib/oxidized/model/openbsd.rb +63 -0
- data/lib/oxidized/model/opengear.rb +3 -5
- data/lib/oxidized/model/openwrt.rb +78 -0
- data/lib/oxidized/model/opnsense.rb +19 -0
- data/lib/oxidized/model/os10.rb +46 -0
- data/lib/oxidized/model/outputs.rb +5 -7
- data/lib/oxidized/model/panos.rb +11 -12
- data/lib/oxidized/model/pfsense.rb +11 -6
- data/lib/oxidized/model/planet.rb +14 -17
- data/lib/oxidized/model/powerconnect.rb +24 -19
- data/lib/oxidized/model/procurve.rb +43 -11
- data/lib/oxidized/model/purityos.rb +12 -0
- data/lib/oxidized/model/qtech.rb +41 -0
- data/lib/oxidized/model/quantaos.rb +4 -6
- data/lib/oxidized/model/raisecom.rb +19 -0
- data/lib/oxidized/model/routeros.rb +26 -8
- data/lib/oxidized/model/saos.rb +1 -2
- data/lib/oxidized/model/screenos.rb +8 -11
- data/lib/oxidized/model/sgos.rb +45 -0
- data/lib/oxidized/model/siklu.rb +1 -3
- data/lib/oxidized/model/slxos.rb +59 -0
- data/lib/oxidized/model/smartax.rb +25 -0
- data/lib/oxidized/model/sonicos.rb +51 -0
- data/lib/oxidized/model/speedtouch.rb +34 -0
- data/lib/oxidized/model/sros.rb +96 -0
- data/lib/oxidized/model/stoneos.rb +32 -0
- data/lib/oxidized/model/supermicro.rb +6 -41
- data/lib/oxidized/model/tdre.rb +30 -0
- data/lib/oxidized/model/telco.rb +24 -0
- data/lib/oxidized/model/timos.rb +6 -114
- data/lib/oxidized/model/tmos.rb +6 -3
- data/lib/oxidized/model/tplink.rb +11 -11
- data/lib/oxidized/model/trango.rb +21 -42
- data/lib/oxidized/model/ucs.rb +30 -0
- data/lib/oxidized/model/viptela.rb +29 -0
- data/lib/oxidized/model/voltaire.rb +9 -12
- data/lib/oxidized/model/voss.rb +17 -6
- data/lib/oxidized/model/vrp.rb +11 -6
- data/lib/oxidized/model/vyatta.rb +8 -6
- data/lib/oxidized/model/weos.rb +20 -0
- data/lib/oxidized/model/xos.rb +20 -8
- data/lib/oxidized/model/zhoneolt.rb +2 -2
- data/lib/oxidized/model/zynos.rb +1 -3
- data/lib/oxidized/model/zynoscli.rb +36 -0
- data/lib/oxidized/model/zynosgs.rb +38 -0
- data/lib/oxidized/node/stats.rb +33 -8
- data/lib/oxidized/node.rb +86 -95
- data/lib/oxidized/nodes.rb +48 -44
- data/lib/oxidized/output/file.rb +32 -37
- data/lib/oxidized/output/git.rb +138 -153
- data/lib/oxidized/output/gitcrypt.rb +228 -242
- data/lib/oxidized/output/http.rb +35 -34
- data/lib/oxidized/output/output.rb +2 -3
- data/lib/oxidized/source/csv.rb +50 -44
- data/lib/oxidized/source/http.rb +58 -58
- data/lib/oxidized/source/source.rb +9 -10
- data/lib/oxidized/source/sql.rb +47 -45
- data/lib/oxidized/string.rb +18 -14
- data/lib/oxidized/version.rb +17 -1
- data/lib/oxidized/worker.rb +72 -33
- data/oxidized.gemspec +20 -19
- metadata +180 -36
- data/.ruby-version +0 -1
- data/Gemfile.lock +0 -44
data/extra/syslog.rb
CHANGED
@@ -27,45 +27,46 @@ require 'resolv'
|
|
27
27
|
require_relative 'rest_client'
|
28
28
|
|
29
29
|
module Oxidized
|
30
|
-
|
31
30
|
require 'asetus'
|
32
31
|
class Config
|
33
|
-
Root
|
32
|
+
Root = File.join ENV['HOME'], '.config', 'oxidized'
|
34
33
|
end
|
35
34
|
|
36
|
-
CFGS = Asetus.new :
|
35
|
+
CFGS = Asetus.new name: 'oxidized', load: false, key_to_s: true
|
37
36
|
CFGS.default.syslogd.port = 514
|
38
37
|
CFGS.default.syslogd.file = 'messages'
|
39
38
|
CFGS.default.syslogd.resolve = true
|
39
|
+
CFGS.default.syslogd.dns_map = {
|
40
|
+
'(.*)\.strip\.this\.domain\.com' => '\\1',
|
41
|
+
'(.*)\.also\.this\.net' => '\\1'
|
42
|
+
}
|
40
43
|
|
41
44
|
begin
|
42
45
|
CFGS.load
|
43
|
-
rescue => error
|
46
|
+
rescue StandardError => error
|
44
47
|
raise InvalidConfig, "Error loading config: #{error.message}"
|
45
48
|
ensure
|
46
|
-
CFG = CFGS.cfg
|
49
|
+
CFG = CFGS.cfg # convenienence, instead of Config.cfg.password, CFG.password
|
47
50
|
end
|
48
51
|
|
49
52
|
class SyslogMonitor
|
50
|
-
NAME_MAP = {
|
51
|
-
/(.*)\.ip\.tdc\.net/ => '\1',
|
52
|
-
/(.*)\.ip\.fi/ => '\1',
|
53
|
-
}
|
54
53
|
MSG = {
|
55
|
-
:
|
56
|
-
:
|
57
|
-
:
|
58
|
-
:
|
59
|
-
|
54
|
+
ios: /%SYS-(SW[0-9]+-)?5-CONFIG_I:/,
|
55
|
+
junos: 'UI_COMMIT:',
|
56
|
+
eos: /%SYS-5-CONFIG_I:/,
|
57
|
+
nxos: /%VSHD-5-VSHD_SYSLOG_CONFIG_I:/,
|
58
|
+
aruba: 'Notice-Type=\'Running'
|
59
|
+
}.freeze
|
60
60
|
|
61
61
|
class << self
|
62
|
-
def udp
|
62
|
+
def udp(port = Oxidized::CFG.syslogd.port, listen = 0)
|
63
63
|
io = UDPSocket.new
|
64
64
|
io.bind listen, port
|
65
65
|
new io, :udp
|
66
66
|
end
|
67
|
-
|
68
|
-
|
67
|
+
|
68
|
+
def file(syslog_file = Oxidized::CFG.syslogd.file)
|
69
|
+
io = File.open syslog_file, 'r'
|
69
70
|
io.seek 0, IO::SEEK_END
|
70
71
|
new io, :file
|
71
72
|
end
|
@@ -73,43 +74,47 @@ module Oxidized
|
|
73
74
|
|
74
75
|
private
|
75
76
|
|
76
|
-
def initialize
|
77
|
+
def initialize(io, mode = :udp)
|
77
78
|
@mode = mode
|
78
79
|
run io
|
79
80
|
end
|
80
81
|
|
81
|
-
def rest
|
82
|
+
def rest(opt)
|
82
83
|
Oxidized::RestClient.next opt
|
83
84
|
end
|
84
85
|
|
85
|
-
def ios
|
86
|
+
def ios(log, index, **opts)
|
86
87
|
# TODO: we need to fetch 'ip/name' in mode == :file here
|
87
|
-
user = log[
|
88
|
-
from = log[-1][1..-2]
|
89
|
-
|
90
|
-
:name => getname(ip) )
|
88
|
+
opts[:user] = log[index + 5]
|
89
|
+
opts[:from] = log[-1][1..-2]
|
90
|
+
opts
|
91
91
|
end
|
92
|
+
alias nxos ios
|
93
|
+
alias eos ios
|
92
94
|
|
93
|
-
def
|
95
|
+
def junos(log, index, **opts)
|
94
96
|
# TODO: we need to fetch 'ip/name' in mode == :file here
|
95
|
-
user = log[
|
96
|
-
msg
|
97
|
-
msg
|
98
|
-
|
99
|
-
|
97
|
+
opts[:user] = log[index + 2][1..-2]
|
98
|
+
opts[:msg] = log[(index + 6)..-1].join(' ')[10..-2]
|
99
|
+
opts.delete(:msg) if opts[:msg] == 'none'
|
100
|
+
opts
|
101
|
+
end
|
102
|
+
|
103
|
+
def aruba(log, index, **opts)
|
104
|
+
opts.merge user: log[index + 2].split('=')[4].split(',')[0][1..-2]
|
100
105
|
end
|
101
106
|
|
102
|
-
def handle_log
|
107
|
+
def handle_log(log, ipaddr)
|
103
108
|
log = log.to_s.split ' '
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
jnpr ip, log, i
|
109
|
+
index, vendor = MSG.find do |key, value|
|
110
|
+
index = log.find_index { |e| e.match value }
|
111
|
+
break index, key if index
|
108
112
|
end
|
113
|
+
rest send(vendor, log, index, ip: ipaddr, name: getname(ipaddr), model: vendor.to_s) if index
|
109
114
|
end
|
110
115
|
|
111
|
-
def run
|
112
|
-
|
116
|
+
def run(io)
|
117
|
+
loop do
|
113
118
|
log = select [io]
|
114
119
|
log, ip = log.first.first, nil
|
115
120
|
if @mode == :udp
|
@@ -127,12 +132,12 @@ module Oxidized
|
|
127
132
|
end
|
128
133
|
end
|
129
134
|
|
130
|
-
def getname
|
135
|
+
def getname(ipaddr)
|
131
136
|
if Oxidized::CFG.syslogd.resolve == false
|
132
|
-
|
137
|
+
ipaddr
|
133
138
|
else
|
134
|
-
name = (Resolv.getname
|
135
|
-
|
139
|
+
name = (Resolv.getname ipaddr.to_s rescue ipaddr)
|
140
|
+
Oxidized::CFG.syslogd.dns_map.each { |re, sub| name.sub! Regexp.new(re.to_s), sub }
|
136
141
|
name
|
137
142
|
end
|
138
143
|
end
|
@@ -140,4 +145,4 @@ module Oxidized
|
|
140
145
|
end
|
141
146
|
|
142
147
|
Oxidized::SyslogMonitor.udp
|
143
|
-
#Oxidized::SyslogMonitor.file '/var/log/poop'
|
148
|
+
# Oxidized::SyslogMonitor.file '/var/log/poop'
|
data/lib/oxidized/cli.rb
CHANGED
@@ -2,15 +2,16 @@ module Oxidized
|
|
2
2
|
class CLI
|
3
3
|
require 'slop'
|
4
4
|
require 'oxidized'
|
5
|
+
require 'English'
|
5
6
|
|
6
7
|
def run
|
7
8
|
check_pid
|
8
9
|
Process.daemon if @opts[:daemonize]
|
9
10
|
write_pid
|
10
11
|
begin
|
11
|
-
Oxidized.logger.info "Oxidized starting, running as pid #{
|
12
|
+
Oxidized.logger.info "Oxidized starting, running as pid #{$PROCESS_ID}"
|
12
13
|
Oxidized.new
|
13
|
-
rescue => error
|
14
|
+
rescue StandardError => error
|
14
15
|
crash error
|
15
16
|
raise
|
16
17
|
end
|
@@ -27,9 +28,9 @@ module Oxidized
|
|
27
28
|
@pidfile = File.expand_path(Oxidized.config.pid)
|
28
29
|
end
|
29
30
|
|
30
|
-
def crash
|
31
|
+
def crash(error)
|
31
32
|
Oxidized.logger.fatal "Oxidized crashed, crashfile written in #{Config::Crash}"
|
32
|
-
open Config::Crash, 'w' do |file|
|
33
|
+
File.open Config::Crash, 'w' do |file|
|
33
34
|
file.puts '-' * 50
|
34
35
|
file.puts Time.now.utc
|
35
36
|
file.puts error.message + ' [' + error.class.to_s + ']'
|
@@ -40,53 +41,62 @@ module Oxidized
|
|
40
41
|
end
|
41
42
|
|
42
43
|
def parse_opts
|
43
|
-
opts = Slop.
|
44
|
-
on 'd', 'debug', 'turn on debugging'
|
45
|
-
on 'daemonize',
|
46
|
-
on '
|
47
|
-
puts
|
44
|
+
opts = Slop.parse do |opt|
|
45
|
+
opt.on '-d', '--debug', 'turn on debugging'
|
46
|
+
opt.on '--daemonize', 'Daemonize/fork the process'
|
47
|
+
opt.on '-h', '--help', 'show usage' do
|
48
|
+
puts opt
|
49
|
+
exit
|
50
|
+
end
|
51
|
+
opt.on '--show-exhaustive-config', 'output entire configuration, including defaults' do
|
52
|
+
asetus = Config.load
|
53
|
+
puts asetus.to_yaml asetus.cfg
|
54
|
+
Kernel.exit
|
55
|
+
end
|
56
|
+
opt.on '-v', '--version', 'show version' do
|
57
|
+
puts Oxidized::VERSION_FULL
|
48
58
|
Kernel.exit
|
49
59
|
end
|
50
60
|
end
|
51
|
-
[opts.
|
61
|
+
[opts.arguments, opts]
|
52
62
|
end
|
53
63
|
|
54
|
-
|
55
|
-
@pidfile
|
56
|
-
end
|
64
|
+
attr_reader :pidfile
|
57
65
|
|
58
66
|
def pidfile?
|
59
67
|
!!pidfile
|
60
68
|
end
|
61
69
|
|
62
70
|
def write_pid
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
+
return unless pidfile?
|
72
|
+
|
73
|
+
begin
|
74
|
+
File.open(pidfile, ::File::CREAT | ::File::EXCL | ::File::WRONLY) { |f| f.write(Process.pid.to_s) }
|
75
|
+
at_exit { File.delete(pidfile) if File.exist?(pidfile) }
|
76
|
+
rescue Errno::EEXIST
|
77
|
+
check_pid
|
78
|
+
retry
|
71
79
|
end
|
72
80
|
end
|
73
81
|
|
74
82
|
def check_pid
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
+
return unless pidfile?
|
84
|
+
|
85
|
+
case pid_status(pidfile)
|
86
|
+
when :running, :not_owned
|
87
|
+
puts "A server is already running. Check #{pidfile}"
|
88
|
+
exit(1)
|
89
|
+
when :dead
|
90
|
+
File.delete(pidfile)
|
83
91
|
end
|
84
92
|
end
|
85
93
|
|
86
94
|
def pid_status(pidfile)
|
87
|
-
return :exited unless File.
|
95
|
+
return :exited unless File.exist?(pidfile)
|
96
|
+
|
88
97
|
pid = ::File.read(pidfile).to_i
|
89
|
-
return :dead if pid
|
98
|
+
return :dead if pid.zero?
|
99
|
+
|
90
100
|
Process.kill(0, pid)
|
91
101
|
:running
|
92
102
|
rescue Errno::ESRCH
|
data/lib/oxidized/config/vars.rb
CHANGED
@@ -1,19 +1,14 @@
|
|
1
1
|
module Oxidized::Config::Vars
|
2
2
|
# convenience method for accessing node, group or global level user variables
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
3
|
+
def vars(name)
|
4
|
+
if @node.vars&.has_key?(name)
|
5
|
+
@node.vars[name]
|
6
|
+
elsif Oxidized.config.groups.has_key?(@node.group) && Oxidized.config.groups[@node.group].vars.has_key?(name.to_s)
|
7
|
+
Oxidized.config.groups[@node.group].vars[name.to_s]
|
8
|
+
elsif Oxidized.config.models.has_key(@node.model.class.name.to_s.downcase) && Oxidized.config.models[@node.model.class.name.to_s.downcase].vars.has_key?(name.to_s)
|
9
|
+
Oxidized.config.models[@node.model.class.name.to_s.downcase].vars[name.to_s]
|
10
|
+
elsif Oxidized.config.vars.has_key?(name.to_s)
|
11
|
+
Oxidized.config.vars[name.to_s]
|
10
12
|
end
|
11
|
-
if Oxidized.config.models.has_key?(@node.model.class.name.to_s.downcase)
|
12
|
-
if Oxidized.config.models[@node.model.class.name.to_s.downcase].vars.has_key?(name.to_s)
|
13
|
-
r ||= Oxidized.config.models[@node.model.class.name.to_s.downcase].vars[name.to_s]
|
14
|
-
end
|
15
|
-
end
|
16
|
-
r ||= Oxidized.config.vars[name.to_s] if Oxidized.config.vars.has_key?(name.to_s)
|
17
|
-
r
|
18
13
|
end
|
19
14
|
end
|
data/lib/oxidized/config.rb
CHANGED
@@ -4,22 +4,23 @@ module Oxidized
|
|
4
4
|
class InvalidConfig < OxidizedError; end
|
5
5
|
class Config
|
6
6
|
Root = ENV['OXIDIZED_HOME'] || File.join(ENV['HOME'], '.config', 'oxidized')
|
7
|
-
Crash = File.join Root, 'crash'
|
8
|
-
Log = File.join Root, 'logs'
|
9
|
-
InputDir = File.join Directory, %w
|
10
|
-
OutputDir = File.join Directory, %w
|
11
|
-
ModelDir = File.join Directory, %w
|
12
|
-
SourceDir = File.join Directory, %w
|
13
|
-
HookDir = File.join Directory, %w
|
7
|
+
Crash = File.join(ENV['OXIDIZED_LOGS'] || Root, 'crash')
|
8
|
+
Log = File.join(ENV['OXIDIZED_LOGS'] || Root, 'logs')
|
9
|
+
InputDir = File.join Directory, %w[lib oxidized input]
|
10
|
+
OutputDir = File.join Directory, %w[lib oxidized output]
|
11
|
+
ModelDir = File.join Directory, %w[lib oxidized model]
|
12
|
+
SourceDir = File.join Directory, %w[lib oxidized source]
|
13
|
+
HookDir = File.join Directory, %w[lib oxidized hook]
|
14
14
|
Sleep = 1
|
15
15
|
|
16
|
-
def self.load(cmd_opts={})
|
16
|
+
def self.load(cmd_opts = {})
|
17
17
|
asetus = Asetus.new(name: 'oxidized', load: false, key_to_s: true)
|
18
18
|
Oxidized.asetus = asetus
|
19
19
|
|
20
20
|
asetus.default.username = 'username'
|
21
21
|
asetus.default.password = 'password'
|
22
22
|
asetus.default.model = 'junos'
|
23
|
+
asetus.default.resolve_dns = true # if false, don't resolve DNS to IP
|
23
24
|
asetus.default.interval = 3600
|
24
25
|
asetus.default.use_syslog = false
|
25
26
|
asetus.default.debug = false
|
@@ -34,21 +35,27 @@ module Oxidized
|
|
34
35
|
asetus.default.models = {} # model level configuration
|
35
36
|
asetus.default.pid = File.join(Oxidized::Config::Root, 'pid')
|
36
37
|
|
37
|
-
asetus.default.
|
38
|
-
asetus.default.
|
39
|
-
|
38
|
+
asetus.default.crash.directory = File.join(Oxidized::Config::Root, 'crashes')
|
39
|
+
asetus.default.crash.hostnames = false
|
40
|
+
|
41
|
+
asetus.default.stats.history_size = 10
|
42
|
+
asetus.default.input.default = 'ssh, telnet'
|
43
|
+
asetus.default.input.debug = false # or String for session log file
|
44
|
+
asetus.default.input.ssh.secure = false # complain about changed certs
|
45
|
+
asetus.default.input.ftp.passive = true # ftp passive mode
|
46
|
+
asetus.default.input.utf8_encoded = true # configuration is utf8 encoded or ascii-8bit
|
40
47
|
|
41
48
|
asetus.default.output.default = 'file' # file, git
|
42
49
|
asetus.default.source.default = 'csv' # csv, sql
|
43
50
|
|
44
51
|
asetus.default.model_map = {
|
45
|
-
'cisco' => 'ios',
|
46
52
|
'juniper' => 'junos',
|
53
|
+
'cisco' => 'ios'
|
47
54
|
}
|
48
55
|
|
49
56
|
begin
|
50
57
|
asetus.load # load system+user configs, merge to Config.cfg
|
51
|
-
rescue => error
|
58
|
+
rescue StandardError => error
|
52
59
|
raise InvalidConfig, "Error loading config: #{error.message}"
|
53
60
|
end
|
54
61
|
|
data/lib/oxidized/core.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Oxidized
|
2
2
|
class << self
|
3
|
-
def new
|
3
|
+
def new(*args)
|
4
4
|
Core.new args
|
5
5
|
end
|
6
6
|
end
|
@@ -8,12 +8,13 @@ module Oxidized
|
|
8
8
|
class Core
|
9
9
|
class NoNodesFound < OxidizedError; end
|
10
10
|
|
11
|
-
def initialize
|
11
|
+
def initialize(_args)
|
12
12
|
Oxidized.mgr = Manager.new
|
13
13
|
Oxidized.Hooks = HookManager.from_config(Oxidized.config)
|
14
|
-
nodes
|
15
|
-
raise NoNodesFound, 'source returns no usable nodes' if nodes.size
|
16
|
-
|
14
|
+
nodes = Nodes.new
|
15
|
+
raise NoNodesFound, 'source returns no usable nodes' if nodes.size.zero?
|
16
|
+
|
17
|
+
@worker = Worker.new nodes
|
17
18
|
trap('HUP') { nodes.load }
|
18
19
|
if Oxidized.config.rest?
|
19
20
|
begin
|
@@ -22,7 +23,7 @@ module Oxidized
|
|
22
23
|
raise OxidizedError, 'oxidized-web not found: sudo gem install oxidized-web - \
|
23
24
|
or disable web support by setting "rest: false" in your configuration'
|
24
25
|
end
|
25
|
-
@rest
|
26
|
+
@rest = API::Web.new nodes, Oxidized.config.rest
|
26
27
|
@rest.run
|
27
28
|
end
|
28
29
|
run
|
@@ -32,10 +33,7 @@ module Oxidized
|
|
32
33
|
|
33
34
|
def run
|
34
35
|
Oxidized.logger.debug "lib/oxidized/core.rb: Starting the worker..."
|
35
|
-
while
|
36
|
-
@worker.work
|
37
|
-
sleep Config::Sleep
|
38
|
-
end
|
36
|
+
@worker.work while sleep Config::Sleep
|
39
37
|
end
|
40
38
|
end
|
41
39
|
end
|
data/lib/oxidized/hook/awssns.rb
CHANGED
@@ -10,18 +10,17 @@ class AwsSns < Oxidized::Hook
|
|
10
10
|
sns = Aws::SNS::Resource.new(region: cfg.region)
|
11
11
|
topic = sns.topic(cfg.topic_arn)
|
12
12
|
message = {
|
13
|
-
:
|
13
|
+
event: ctx.event.to_s
|
14
14
|
}
|
15
15
|
if ctx.node
|
16
16
|
message.merge!(
|
17
|
-
:
|
18
|
-
:
|
19
|
-
:
|
17
|
+
group: ctx.node.group.to_s,
|
18
|
+
model: ctx.node.model.class.name.to_s.downcase,
|
19
|
+
node: ctx.node.name.to_s
|
20
20
|
)
|
21
21
|
end
|
22
|
-
topic.publish(
|
22
|
+
topic.publish(
|
23
23
|
message: message.to_json
|
24
|
-
|
24
|
+
)
|
25
25
|
end
|
26
|
-
|
27
26
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'cisco_spark'
|
2
|
+
|
3
|
+
# defaults to posting a diff, if messageformat is supplied them a message will be posted too
|
4
|
+
# diff defaults to true
|
5
|
+
# Modified from slackdiff
|
6
|
+
|
7
|
+
class CiscoSparkDiff < Oxidized::Hook
|
8
|
+
def validate_cfg!
|
9
|
+
raise KeyError, 'hook.accesskey is required' unless cfg.has_key?('accesskey')
|
10
|
+
raise KeyError, 'hook.space is required' unless cfg.has_key?('space')
|
11
|
+
end
|
12
|
+
|
13
|
+
def run_hook(ctx)
|
14
|
+
return unless ctx.node
|
15
|
+
return unless ctx.event.to_s == "post_store"
|
16
|
+
|
17
|
+
log "Connecting to Cisco Spark"
|
18
|
+
CiscoSpark.configure do |config|
|
19
|
+
config.api_key = cfg.accesskey
|
20
|
+
config.proxy = cfg.proxy if cfg.has_key?('proxy')
|
21
|
+
end
|
22
|
+
room = CiscoSpark::Room.new(id: cfg.space)
|
23
|
+
log "Connected"
|
24
|
+
|
25
|
+
if cfg.has_key?("diff") ? cfg.diff : true
|
26
|
+
gitoutput = ctx.node.output.new
|
27
|
+
diff = gitoutput.get_diff ctx.node, ctx.node.group, ctx.commitref, nil
|
28
|
+
title = ctx.node.name.to_s
|
29
|
+
log "Posting diff as snippet to #{cfg.space}"
|
30
|
+
room.send_message CiscoSpark::Message.new(text: 'Device ' + title + ' modified:' + "\n" + diff[:patch].lines.to_a[4..-1].join)
|
31
|
+
end
|
32
|
+
|
33
|
+
if cfg.message?
|
34
|
+
log cfg.message
|
35
|
+
msg = cfg.message % { node: ctx.node.name.to_s, group: ctx.node.group.to_s, commitref: ctx.commitref, model: ctx.node.model.class.name.to_s.downcase }
|
36
|
+
log msg
|
37
|
+
log "Posting message to #{cfg.space}"
|
38
|
+
room.send_message CiscoSpark::Message.new(text: msg)
|
39
|
+
end
|
40
|
+
|
41
|
+
log "Finished"
|
42
|
+
end
|
43
|
+
end
|
data/lib/oxidized/hook/exec.rb
CHANGED
@@ -12,40 +12,37 @@ class Exec < Oxidized::Hook
|
|
12
12
|
if cfg.has_key? "timeout"
|
13
13
|
@timeout = cfg.timeout
|
14
14
|
raise "invalid timeout value" unless @timeout.is_a?(Integer) &&
|
15
|
-
@timeout
|
15
|
+
@timeout.positive?
|
16
16
|
end
|
17
17
|
|
18
|
-
if cfg.has_key? "async"
|
19
|
-
@async = !!cfg.async
|
20
|
-
end
|
18
|
+
@async = !!cfg.async if cfg.has_key? "async"
|
21
19
|
|
22
20
|
if cfg.has_key? "cmd"
|
23
21
|
@cmd = cfg.cmd
|
24
22
|
raise "invalid cmd value" unless @cmd.is_a?(String) || @cmd.is_a?(Array)
|
25
23
|
end
|
26
|
-
|
27
24
|
rescue RuntimeError => e
|
28
25
|
raise ArgumentError,
|
29
|
-
|
26
|
+
"#{self.class.name}: configuration invalid: #{e.message}"
|
30
27
|
end
|
31
28
|
|
32
|
-
def run_hook
|
29
|
+
def run_hook(ctx)
|
33
30
|
env = make_env ctx
|
34
31
|
log "Execute: #{@cmd.inspect}", :debug
|
35
32
|
th = Thread.new do
|
36
33
|
begin
|
37
34
|
run_cmd! env
|
38
|
-
rescue => e
|
35
|
+
rescue StandardError => e
|
39
36
|
raise e unless @async
|
40
37
|
end
|
41
38
|
end
|
42
39
|
th.join unless @async
|
43
40
|
end
|
44
41
|
|
45
|
-
def run_cmd!
|
42
|
+
def run_cmd!(env)
|
46
43
|
pid, status = nil, nil
|
47
44
|
Timeout.timeout(@timeout) do
|
48
|
-
pid = spawn env, @cmd
|
45
|
+
pid = spawn env, @cmd, unsetenv_others: true
|
49
46
|
pid, status = wait2 pid
|
50
47
|
unless status.exitstatus.zero?
|
51
48
|
msg = "#{@cmd.inspect} failed with exit value #{status.exitstatus}"
|
@@ -53,34 +50,32 @@ class Exec < Oxidized::Hook
|
|
53
50
|
raise msg
|
54
51
|
end
|
55
52
|
end
|
56
|
-
rescue
|
53
|
+
rescue Timeout::Error
|
57
54
|
kill "TERM", pid
|
58
55
|
msg = "#{@cmd} timed out"
|
59
56
|
log msg, :error
|
60
|
-
raise
|
57
|
+
raise Timeout::Error, msg
|
61
58
|
end
|
62
59
|
|
63
|
-
def make_env
|
60
|
+
def make_env(ctx)
|
64
61
|
env = {
|
65
62
|
"OX_EVENT" => ctx.event.to_s
|
66
63
|
}
|
67
64
|
if ctx.node
|
68
65
|
env.merge!(
|
69
|
-
"OX_NODE_NAME"
|
70
|
-
"OX_NODE_IP"
|
71
|
-
"OX_NODE_FROM"
|
72
|
-
"OX_NODE_MSG"
|
73
|
-
"OX_NODE_GROUP"
|
74
|
-
"
|
66
|
+
"OX_NODE_NAME" => ctx.node.name.to_s,
|
67
|
+
"OX_NODE_IP" => ctx.node.ip.to_s,
|
68
|
+
"OX_NODE_FROM" => ctx.node.from.to_s,
|
69
|
+
"OX_NODE_MSG" => ctx.node.msg.to_s,
|
70
|
+
"OX_NODE_GROUP" => ctx.node.group.to_s,
|
71
|
+
"OX_NODE_MODEL" => ctx.node.model.class.name,
|
75
72
|
"OX_REPO_COMMITREF" => ctx.commitref.to_s,
|
76
|
-
"OX_REPO_NAME"
|
73
|
+
"OX_REPO_NAME" => ctx.node.repo.to_s
|
77
74
|
)
|
78
75
|
end
|
79
76
|
if ctx.job
|
80
|
-
env.
|
81
|
-
|
82
|
-
"OX_JOB_TIME" => ctx.job.time.to_s,
|
83
|
-
)
|
77
|
+
env["OX_JOB_STATUS"] = ctx.job.status.to_s
|
78
|
+
env["OX_JOB_TIME"] = ctx.job.time.to_s
|
84
79
|
end
|
85
80
|
env
|
86
81
|
end
|
@@ -18,8 +18,8 @@ class GithubRepo < Oxidized::Hook
|
|
18
18
|
result = repo.fetch('origin', [repo.head.name], credentials: credentials)
|
19
19
|
log result.inspect, :debug
|
20
20
|
|
21
|
-
unless result[:total_deltas]
|
22
|
-
log "nothing
|
21
|
+
unless result[:total_deltas].positive?
|
22
|
+
log "nothing received after fetch", :debug
|
23
23
|
return
|
24
24
|
end
|
25
25
|
|
@@ -34,27 +34,27 @@ class GithubRepo < Oxidized::Hook
|
|
34
34
|
return
|
35
35
|
end
|
36
36
|
|
37
|
-
Rugged::Commit.create(repo,
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
})
|
37
|
+
Rugged::Commit.create(repo,
|
38
|
+
parents: [repo.head.target, their_branch.target],
|
39
|
+
tree: merge_index.write_tree(repo),
|
40
|
+
message: "Merge remote-tracking branch '#{their_branch.name}'",
|
41
|
+
update_ref: "HEAD")
|
43
42
|
end
|
44
43
|
|
45
44
|
private
|
46
45
|
|
47
46
|
def credentials
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
47
|
+
Proc.new do |_url, username_from_url, _allowed_types| # rubocop:disable Style/Proc
|
48
|
+
git_user = cfg.has_key?('username') ? cfg.username : (username_from_url || 'git')
|
49
|
+
if cfg.has_key?('password')
|
50
|
+
log "Authenticating using username and password as '#{git_user}'", :debug
|
51
|
+
Rugged::Credentials::UserPassword.new(username: git_user, password: cfg.password)
|
52
|
+
elsif cfg.has_key?('publickey') && cfg.has_key?('privatekey')
|
53
|
+
log "Authenticating using ssh keys as '#{git_user}'", :debug
|
54
|
+
Rugged::Credentials::SshKey.new(username: git_user, publickey: File.expand_path(cfg.publickey), privatekey: File.expand_path(cfg.privatekey), passphrase: ENV["OXIDIZED_SSH_PASSPHRASE"])
|
55
55
|
else
|
56
|
-
log "
|
57
|
-
Rugged::Credentials::SshKeyFromAgent.new(username:
|
56
|
+
log "Authenticating using ssh agent as '#{git_user}'", :debug
|
57
|
+
Rugged::Credentials::SshKeyFromAgent.new(username: git_user)
|
58
58
|
end
|
59
59
|
end
|
60
60
|
end
|